blob: a4970fd8a3da073c733d1165aa77afb508bcd66c [file] [log] [blame]
/*
* Common code for wl command-line swiss-army-knife utility
*
* Broadcom Proprietary and Confidential. Copyright (C) 2017,
* All Rights Reserved.
*
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
* 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.
*
*
* <<Broadcom-WL-IPTag/Proprietary:>>
*
* $Id: wlu.c 663042 2016-11-30 13:48:19Z $
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <wlioctl.h>
#include <wlioctl_utils.h>
#if !defined(TARGETOS_nucleus)
#define CLMDOWNLOAD
#endif
#if defined(DONGLEBUILD)
#include <typedefs.h>
#include <osl.h>
#endif
/* 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"
#include <rte_ioctl.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(linux)
#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>
#include <time.h>
#include <sched.h>
#define TIME_STR_SZ 100 /* string buffer size for timestamp formatting */
#endif /* linux */
#if defined(WLBSSLOAD_REPORT) && defined(linux)
#include <sys/time.h>
#endif /* defined(WLBSSLOAD_REPORT) && defined(linux) */
#ifdef LINUX
#include <inttypes.h>
#endif
#include <miniopt.h>
#include <errno.h>
#if defined SERDOWNLOAD || defined CLMDOWNLOAD
#include <sys/stat.h>
#include <trxhdr.h>
#ifdef SERDOWNLOAD
#include <usbrdl.h>
#endif
#include <stdio.h>
#include <errno.h>
#ifndef WIN32
#include <fcntl.h>
#endif /* WIN32 */
#endif /* SERDOWNLOAD || defined CLMDOWNLOAD */
#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>
#define EVENT_LOG_DUMPER
#include <proto/event_log_tag.h>
#include <sdiovar.h>
#include "wlu_subcounters.h"
/* 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
#if defined(EDK_RELEASE_VERSION) || (EDK_RELEASE_VERSION >= 0x00020000)
extern int wlu_efi_stat(char *filename, struct stat *filest);
extern long wlu_efi_ftell(void *fp);
extern int wlu_efi_fseek(void *fp, long offset, int whence);
extern size_t wlu_efi_fwrite(void *buf, size_t size, size_t nmemb, void *fp);
extern size_t wlu_efi_fread(void *buf, size_t size, size_t nmemb, void *fp);
extern void wlu_efi_fclose(void *fp);
extern void * wlu_efi_fopen(char *filename, char *mode);
#define fopen(filename, mode) (FILE *)wlu_efi_fopen(filename, mode)
#define fread(buf, size, nmemb, fp) wlu_efi_fread(buf, size, nmemb, fp)
#define fwrite(buf, size, nmemb, fp) wlu_efi_fwrite(buf, size, nmemb, fp)
#define fseek(fp, offset, origin) wlu_efi_fseek(fp, offset, origin)
#define ftell(fp) wlu_efi_ftell(fp)
#define stat(fname, filest) wlu_efi_stat(fname, (struct stat *)(filest))
#define fclose(fp) wlu_efi_fclose(fp)
#ifdef stderr
#undef stderr
#define stderr stdout
#endif
#endif /* defined(EDK_RELEASE_VERSION) || (EDK_RELEASE_VERSION >= 0x00020000) */
const char blob_magic_string[] = {'B', 'L', 'O', 'B'};
cmd_func_t wl_int, wl_buf;
const ofdm_rates_t ofdm_rates[] = { /* 6b, 9, 12b, 18, 24b, 36, 48, 54 Mbps */
0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c };
/* ifdef protection since wlc_event_names[] only used under ifdef linux */
#ifdef linux
#define NAME_ENTRY(x) {x, #x}
static const wlu_name_entry_t wlc_event_names[] = {
NAME_ENTRY(WLC_E_SET_SSID),
NAME_ENTRY(WLC_E_JOIN),
NAME_ENTRY(WLC_E_START),
NAME_ENTRY(WLC_E_AUTH),
NAME_ENTRY(WLC_E_AUTH_IND),
NAME_ENTRY(WLC_E_DEAUTH),
NAME_ENTRY(WLC_E_DEAUTH_IND),
NAME_ENTRY(WLC_E_ASSOC),
NAME_ENTRY(WLC_E_ASSOC_IND),
NAME_ENTRY(WLC_E_REASSOC),
NAME_ENTRY(WLC_E_REASSOC_IND),
NAME_ENTRY(WLC_E_DISASSOC),
NAME_ENTRY(WLC_E_DISASSOC_IND),
NAME_ENTRY(WLC_E_QUIET_START),
NAME_ENTRY(WLC_E_QUIET_END),
NAME_ENTRY(WLC_E_BEACON_RX),
NAME_ENTRY(WLC_E_LINK),
NAME_ENTRY(WLC_E_MIC_ERROR),
NAME_ENTRY(WLC_E_NDIS_LINK),
NAME_ENTRY(WLC_E_ROAM),
NAME_ENTRY(WLC_E_TXFAIL),
NAME_ENTRY(WLC_E_PMKID_CACHE),
NAME_ENTRY(WLC_E_RETROGRADE_TSF),
NAME_ENTRY(WLC_E_PRUNE),
NAME_ENTRY(WLC_E_AUTOAUTH),
NAME_ENTRY(WLC_E_EAPOL_MSG),
NAME_ENTRY(WLC_E_SCAN_COMPLETE),
NAME_ENTRY(WLC_E_ADDTS_IND),
NAME_ENTRY(WLC_E_DELTS_IND),
NAME_ENTRY(WLC_E_BCNSENT_IND),
NAME_ENTRY(WLC_E_BCNRX_MSG),
NAME_ENTRY(WLC_E_BCNLOST_MSG),
NAME_ENTRY(WLC_E_ROAM_PREP),
NAME_ENTRY(WLC_E_PFN_NET_FOUND),
NAME_ENTRY(WLC_E_PFN_NET_LOST),
NAME_ENTRY(WLC_E_RESET_COMPLETE),
NAME_ENTRY(WLC_E_JOIN_START),
NAME_ENTRY(WLC_E_ROAM_START),
NAME_ENTRY(WLC_E_ASSOC_START),
NAME_ENTRY(WLC_E_IBSS_ASSOC),
NAME_ENTRY(WLC_E_RADIO),
NAME_ENTRY(WLC_E_PSM_WATCHDOG),
NAME_ENTRY(WLC_E_PROBREQ_MSG),
NAME_ENTRY(WLC_E_SCAN_CONFIRM_IND),
NAME_ENTRY(WLC_E_PSK_SUP),
NAME_ENTRY(WLC_E_COUNTRY_CODE_CHANGED),
NAME_ENTRY(WLC_E_EXCEEDED_MEDIUM_TIME),
NAME_ENTRY(WLC_E_ICV_ERROR),
NAME_ENTRY(WLC_E_UNICAST_DECODE_ERROR),
NAME_ENTRY(WLC_E_MULTICAST_DECODE_ERROR),
NAME_ENTRY(WLC_E_TRACE),
NAME_ENTRY(WLC_E_IF),
NAME_ENTRY(WLC_E_P2P_DISC_LISTEN_COMPLETE),
NAME_ENTRY(WLC_E_RSSI),
NAME_ENTRY(WLC_E_PFN_SCAN_COMPLETE),
NAME_ENTRY(WLC_E_EXTLOG_MSG),
NAME_ENTRY(WLC_E_ACTION_FRAME),
NAME_ENTRY(WLC_E_ACTION_FRAME_COMPLETE),
NAME_ENTRY(WLC_E_PRE_ASSOC_IND),
NAME_ENTRY(WLC_E_PRE_REASSOC_IND),
NAME_ENTRY(WLC_E_CHANNEL_ADOPTED),
NAME_ENTRY(WLC_E_AP_STARTED),
NAME_ENTRY(WLC_E_DFS_AP_STOP),
NAME_ENTRY(WLC_E_DFS_AP_RESUME),
NAME_ENTRY(WLC_E_WAI_STA_EVENT),
NAME_ENTRY(WLC_E_WAI_MSG),
NAME_ENTRY(WLC_E_ESCAN_RESULT),
NAME_ENTRY(WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE),
NAME_ENTRY(WLC_E_PROBRESP_MSG),
NAME_ENTRY(WLC_E_P2P_PROBREQ_MSG),
NAME_ENTRY(WLC_E_DCS_REQUEST),
NAME_ENTRY(WLC_E_FIFO_CREDIT_MAP),
NAME_ENTRY(WLC_E_ACTION_FRAME_RX),
NAME_ENTRY(WLC_E_WAKE_EVENT),
NAME_ENTRY(WLC_E_RM_COMPLETE),
NAME_ENTRY(WLC_E_HTSFSYNC),
NAME_ENTRY(WLC_E_OVERLAY_REQ),
NAME_ENTRY(WLC_E_CSA_COMPLETE_IND),
NAME_ENTRY(WLC_E_EXCESS_PM_WAKE_EVENT),
NAME_ENTRY(WLC_E_PFN_SCAN_NONE),
NAME_ENTRY(WLC_E_PFN_SCAN_ALLGONE),
NAME_ENTRY(WLC_E_GTK_PLUMBED),
NAME_ENTRY(WLC_E_ASSOC_IND_NDIS),
NAME_ENTRY(WLC_E_REASSOC_IND_NDIS),
NAME_ENTRY(WLC_E_ASSOC_REQ_IE),
NAME_ENTRY(WLC_E_ASSOC_RESP_IE),
{ 0, NULL}
};
#endif /* linux */
static cmd_func_t wl_print_deprecate;
static cmd_func_t wl_rssi, wl_gmode;
static cmd_func_t wlu_dump, wlu_dump_clr, wlu_mempool, wlu_srdump, wlu_srvar;
static cmd_func_t wlu_ciswrite, wlu_cisupdate, wlu_cisdump;
static cmd_func_t wl_rate, wl_rate_mrate, wl_bss_max;
static cmd_func_t wl_channel, wl_chanspec, wl_rclass, wl_dfs_ap_move, wl_sc_chan;
static cmd_func_t wl_phy_vcore;
static cmd_func_t wl_dfs_max_safe_tx;
static cmd_func_t wl_radio, wl_version, wl_list, wl_band, wl_bandlist, wl_phylist;
static cmd_func_t wl_join, wl_txpwr, wl_country;
static cmd_func_t wl_out, wl_txpwr1, wl_country_ie_override, wl_echo;
static cmd_func_t wl_radar_status, wl_clear_radar_status;
static cmd_func_t wl_radar_sc_status;
static cmd_func_t wl_get_pktcnt, wl_upgrade;
static cmd_func_t wl_default_rateset;
static cmd_func_t wl_rateset, wl_txbf_rateset;
static cmd_func_t wl_dfs_status;
static cmd_func_t wl_dfs_status_all;
static cmd_func_t wl_get_txpwr_limit, wl_get_current_power;
static cmd_func_t wl_get_txpwr_target_max, wl_get_chanspec_txpwr_max;
static cmd_func_t wl_var_getint;
static cmd_func_t wl_nvdump, wl_nvget, wl_nvset, 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;
static cmd_func_t wl_wlc_ver;
static cmd_func_t wl_assoc_info, wl_wme_counters;
static cmd_func_t wl_rxfifo_counters;
static cmd_func_t wl_eventbitvec, wl_bitvecext;
static cmd_func_t wl_auto_channel_sel;
static cmd_func_t wl_msglevel, wl_plcphdr, wl_macreg, wl_band_elm;
static cmd_func_t wl_rateparam, 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, wl_roam_prof;
#ifdef WLRCC
static cmd_func_t wl_roamchannels;
#endif /* WLRCC */
static cmd_func_t wl_dump_chanlist, wl_measure_req, wl_send_quiet;
static cmd_func_t wl_pm_mute_tx;
static cmd_func_t wl_dump_chanspecs, wl_dump_chanspecs_defset;
static cmd_func_t wl_wsec;
static cmd_func_t wl_channels_in_country;
static cmd_func_t wl_wpa_auth, wl_deauth_rc, wl_bssid, wl_smfstats;
static cmd_func_t 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_revinfo, wl_iov_pktqlog_params;
static cmd_func_t wl_varstr;
static cmd_func_t wl_winver;
#if defined(linux)
static cmd_func_t wl_escan_event_check;
static cmd_func_t wl_escanresults;
static cmd_func_t wl_event_check;
#endif /* linux */
static cmd_func_t wl_hs20_ie;
static cmd_func_t wl_reassoc;
static cmd_func_t wl_overlay;
static cmd_func_t wl_pmkid_info;
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 wme_tx_params;
static cmd_func_t wme_maxbw_params;
static cmd_func_t wl_actframe;
static cmd_func_t wl_antsel;
static cmd_func_t wl_txfifo_sz;
static cmd_func_t wl_mcast_ar;
static cmd_func_t wl_pwrstats;
static cmd_func_t wl_memuse;
int wl_seq_batch_in_client(bool enable);
static cmd_func_t wl_antgain;
static cmd_func_t wl_srchmem;
static cmd_func_t wl_ptk_start;
#ifdef CLMDOWNLOAD
static cmd_func_t wl_clmload;
static cmd_func_t wl_txcapload;
static cmd_func_t wl_txcapctl;
static cmd_func_t wl_txcapdump;
#endif /* CLMDOWNLOAD */
static cmd_func_t wl_calload;
static cmd_func_t wl_caldump;
#ifdef RWL_WIFI
/* Function added to support RWL_WIFI Transport */
static cmd_func_t wl_wifiserver;
#endif
static cmd_func_t wl_cca_get_stats;
static cmd_func_t wl_txdelay_params;
static cmd_func_t wl_intfer_params;
static cmd_func_t wl_rpmt;
static cmd_func_t wl_sarlimit;
static cmd_func_t wl_ie;
static cmd_func_t wl_ccode_info;
#ifdef SERDOWNLOAD
static cmd_func_t dhd_upload;
int debug = 0;
#endif
static cmd_func_t wl_wds_ap_ifname;
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;
#ifdef SR_DEBUG
static cmd_func_t wl_dump_pmu;
#endif /* SR_DEBUG */
#ifdef TBTT_OFFSET_STAT
static cmd_func_t wl_tbtt_offset_stat;
#endif /* TBTT_OFFSET_STAT */
static cmd_func_t wl_bss_peer_info;
static cmd_func_t wl_aibss_txfail_config;
static cmd_func_t wl_setiproute;
static cmd_func_t wl_desired_bssid;
static cmd_func_t wl_interface_create_action;
static cmd_func_t wl_interface_remove_action;
static cmd_func_t wl_macdbg_pmac;
static cmd_func_t wl_svmp_mem;
static cmd_func_t wl_mu_rate;
static cmd_func_t wl_mu_group;
static cmd_func_t wl_mu_policy;
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
static cmd_func_t wl_svmp_sampcol;
#endif
static cmd_func_t wl_macregx;
static cmd_func_t wl_scanmac;
cmd_func_t wl_hostip;
cmd_func_t wl_hostipv6_extended;
static cmd_func_t wl_hc;
static cmd_func_t wl_wake_timer;
static cmd_func_t wl_idauth;
static cmd_func_t wl_netx_ifconfig;
static int wl_idauth_config_set(void *wl, uint16 category, char **argv);
static int wl_idauth_config_get(void *wl, uint16 category, char **argv);
static int wl_idauth_dump_counters(void *wl, uint16 category, char **argv);
static int wl_idauth_dump_peer_info(void *wl, uint16 category, char **argv);
static int wlu_dump_phytbls(void *wl, char *dump_buf);
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);
void wl_txpwr_ppr_print_row(const char* label, int8 chains, int8 bw, bool vb,
int8** rates, uint rate_index);
void wl_txpwr_ppr_get_rateset(ppr_t* pprptr, ppr_rate_type_t type,
clm_rate_group_id_t gid, wl_tx_bw_t bw, int8* rateset);
static int wl_array_check_val(int8 *pwr, uint count, int8 val);
static int wl_parse_rateset(void *wl, wl_rateset_args_t* rs, char **argv);
static void wl_print_vhtmcsset(uint16 *mcsset);
static void dump_networks(char *buf);
static void wl_dump_wpa_rsn_ies(uint8* cp, uint len);
static void wl_rsn_ie_dump(bcm_tlv_t *ie);
static void wl_dump_ext_cap(uint8* cp, uint len);
static void wl_ext_cap_ie_dump(bcm_tlv_t* ext_cap_ie);
static void free_cca_array(cca_congest_channel_req_t **favg, int favg_chan_elts);
static int wl_print_dfs_status(wl_dfs_status_t *dfs_status);
static int wl_print_dfs_sub_status(wl_dfs_sub_status_t *sub);
static int wl_print_dfs_status_all(wl_dfs_status_all_t *dfs_status_all);
static int wl_parse_txbf_rateset(wl_txbf_rateset_t *rs, char **argv);
static void wl_print_txbf_mcsset(char *mcsset, char *prefix);
static void wl_print_txbf_vhtmcsset(uint16 *mcsset, char *prefix);
static cmd_func_t wl_power_sel_params;
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
static cmd_func_t wl_dump_modesw_dyn_bwsw;
#endif
int wlu_get(void *wl, int cmd, void *buf, int len);
int wlu_set(void *wl, int cmd, void *buf, int len);
static cmd_func_t wl_modesw_timecal;
static cmd_func_t wl_bcntrim_stats;
static int fsize(void *fp);
typedef struct wl_config_iovar_s wl_config_iovar_t;
typedef struct nv_s nv_t;
/* 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);
typedef void (wl_config_print_func_t)(wl_config_iovar_t *config_iovar,
wl_config_t *config);
static void wl_bcm_config_print(wl_config_iovar_t *cfg_iovar, wl_config_t *cfg);
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 uint16 wl_qdbm_to_mw(int8 qdbm);
static int8 wl_mw_to_qdbm(uint16 mw);
static void wl_printrate(int val);
static int wl_get_iscan(void *wl, char *buf, uint buf_len);
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 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 int wl_assertlog(void *wl, cmd_t *cmd, char **argv);
static int wl_tsf(void *wl, cmd_t *cmd, char **argv);
static cmd_func_t wl_scb_bs_data;
static int wl_dfs_channel_forced(void *wl, cmd_t *cmd, char **argv);
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);
static int wl_event_log_get(void *wl, cmd_t *cmd, char **argv);
static int wl_sleep_ret_ext(void *wl, cmd_t *cmd, char **argv);
static cmd_func_t wl_pcie_bus_throughput_params;
static cmd_func_t wl_phy_txpwrcap_tbl;
static cmd_func_t wl_sim_pm;
static cmd_func_t wl_utrace_capture;
#ifdef ATE_BUILD
static cmd_func_t wl_gpaio;
#endif
static cmd_func_t wl_nd_ra_limit_intv;
static void dlystat_dump(txdelay_stats_t *txdelay_stats);
static int wl_dlystats(void *wl, cmd_t *cmd, char **argv);
static int wl_dlystats_clear(void *wl, cmd_t *cmd, char **argv);
static int wl_get_tcmstbl_entry(void *wl, cmd_t *cmd, char **argv);
char *ver2str(unsigned int vms, unsigned int vls);
#define BCM_CONFIG_ARRAY_SIZE 10
#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 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)
#define RADIO_2069_CORE_PLL0 (0x4 << 9)
#define RADIO_2069_CORE_PLL1 (0x5 << 9)
#define NUM_CHANSPECS_LIST_SIZE 110 /* chanspecs list size passed to driver */
/* IOCtl version read from targeted driver */
static int ioctl_version;
/* 64 bits aligned allocation */
static union {
char bufdata[WLC_IOCTL_MAXLEN];
uint64 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 */
struct nv_s {
char *name;
uint32 value;
};
struct wl_config_iovar_s {
char *iovar_name;
wl_config_print_func_t *pfunc;
nv_t params[BCM_CONFIG_ARRAY_SIZE];
};
typedef struct {
uint value;
const char *string;
} monitor_promisc_level_msg_t;
#define WL_SCAN_PARAMS_SSID_MAX 10
#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; 2.5, 5, 10, 20, 40, 80, 160"
#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; 2.5, 5, 10, 20, 40, 80, 160"
#define TXBF_RATESET_USAGE \
"Get rateset consisting of OFDM, HT and VHT rates, and Broadcom-to-Broadcom\n" \
"\tgroup of OFDM, HT and VHT rates by issuing command with no arguments.\n" \
"\tOFDM rates printed are in Mbps, and each Basic rate in OFDM list is marked\n" \
"\tby (b) behind it. Example: full list of OFDM rates:\n" \
"\t\t6(b) 9 12(b) 18 24(b) 36 48 54\n" \
"\twhere 6, 12 and 24 are Basic rates.\n" \
"\n" \
"\tSet synopsis:\n" \
"\t\twl txbf_rateset < [ofdm_rate_list] [options ...] >\n" \
"\tOFDM rate specification does not need to mark Basic rates because Basic\n" \
"\trates are automatically recognized.\n" \
"\tOptions are processed in order; thus redundant instances of an option will\n" \
"\tresult in only the last instance taking effect for that option.\n" \
"\tOptions:\n" \
"\t-m <MCS_bitmask> ...\n" \
"\t\tSet HT rates by bitmask bytes, each ranges from 00 through ff, where\n" \
"\t\tthe least significant bit is MCS0.\n" \
"\t\tExample: '-m 3f 01' specifies HT rates MCS0 - MCS5 and MCS8.\n" \
"\n" \
"\t-v <VHT_bitmask> ...\n" \
"\t\tSet VHT rates for each supported count of spatial streams.\n" \
"\t\tExample: '-v 3ff 1ff ff' specifies VHT rates: MCS0 - MCS9 for 1 stream,\n" \
"\t\tMCS0 - MCS8 for 2 streams, and MCS0 - MCS7 for 3 streams.\n" \
"\n" \
"\t-b\n" \
"\t\tSet for Broadcom-to-Broadcom group of rates. Otherwise without\n" \
"\t\tthe -b option, the standard group of rates are set accordingly.\n"
#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"
/* the default behavior is batching in driver,
* to indicate client batching, users should specify --interactive and --clientbatch
*/
static bool batch_in_client;
/* The wl_config_iovar_list structure is used to define config iovars. Config iovars can be in
* either an auto mode or in an override mode. If it is in auto mode, the status of the iovar
* is determined automatically. In override mode, the status is passed as a parameter to the
* iovar. If a new config iovar is getting added, it can either reuse the last entry in the
* list if parameters match, or add a new entry. If you are adding a new entry, make sure it is
* added before the last entry.
* In each row, the last entry of name-value params must have NULL
*/
wl_config_iovar_t wl_config_iovar_list[] = {
{ "rsdb_mode", wl_bcm_config_print, {{"auto", -1}, {"mimo", 0}, {"rsdb", 1}, {"80p80", 2},
{NULL, 0}}},
{ NULL, wl_bcm_config_print, {{"auto", -1}, {"off", 0}, {"disable", 0}, {"on", 1},
{"enable", 1}, {NULL, 0}}},
};
/* 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[] = {
{ "debug_crash", wlu_reg2args, WLC_GET_VAR, WLC_SET_VAR,
"debug crash - TYPE DELAYED"},
{ "winver", wl_winver, -1, -1,
"get Windows OS and driver version information" },
{ "ver", wl_version, -1, -1,
"get version information" },
{ "cmds", wl_list, -1, -1,
"generate a short list of available commands"},
{ "ioctl_echo", wl_echo, -1, WLC_ECHO,
"check ioctl functionality" },
{ "up", wl_void, -1, WLC_UP,
"reinitialize and mark adapter up (operational)" },
{ "down", wl_void, -1, WLC_DOWN,
"reset and mark adapter down (disabled)" },
{ "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" },
{ "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" },
{ "dump_clear", wlu_dump_clr, -1, WLC_SET_VAR,
"Clear a specific category of counters/stats.\n"
"\tUsage: dump_clear <category-name>\n"},
{ "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" },
{ "ciswrite", wlu_ciswrite, -1, WLC_SET_VAR,
"Write specified <file> to the SDIO/PCIe CIS source (either SROM or OTP)"
"\tUsage: ciswrite [-p|--pciecis] <file>\n"
"\t-p|--pciecis -- Write OTP for PCIe full-dongle"},
{ "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)"},
{ "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_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" },
{ "msglevel", wl_msglevel, WLC_GET_VAR, WLC_SET_VAR,
"set driver console debugging message bitvector\n"
"\ttype \'wl msglevel ?\' 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,
"Configure BSS type or query Infra mode.\n"
"Configure BSS type for next BSS Start or Join operation:\n"
"\t0 (IBSS)\n"
"\t1 (Infra BSS)\n"
"\t2 (Any BSS, for Join only)\n"
"\t3 (Mesh BSS)\n"
"Query the Infra mode of the current BSS:\n"
"\t0 (IBSS)\n"
"\t1 (Infra BSS)\n"
"Note: use \"wl infra_configuration\" to query the configuration.\n"},
{ "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 " },
{ "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, 144,\n"
"\t\t149, 153, 157, 161,\n"
"\t\t184, 188, 192, 196, 200, 204, 208, 212, 216"},
{ "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, 80, 160, or 80+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"
"\t160MHz: [5g]<channel>/160\n"
"\t80+80MHz: [5g]<channel>/80+80/<1st80channel>-<2nd80channel>\n"
"\toptional band 2g or 5g, default to 2g if channel <= 14\n"
"\tchannel number (0-200)\n"
"\tbandwidth, 2.5, 5, 10, 20, 40, 80, 160, or 80+80 default 20\n"
"\tprimary sideband for 40MHz on 2g, l=lower, u=upper\n"
"\t<1st80Channel>, <2nd80Channel> Required for 80+80, otherwise not allowed. "
"These fields specify the center channel of the first and second 80MHz band.\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"},
{ "sc_chan", wl_sc_chan, 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, 2.5, 5, 10, 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"},
{ "phy_vcore", wl_phy_vcore, WLC_GET_VAR, -1,
"get virtual core related capabilities\n"},
{ "rclass", wl_rclass, WLC_GET_VAR, -1,
"Get operation class:\n"
"\t chanspec \n"},
{ "dfs_channel_forced", wl_dfs_channel_forced, 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\n"
"Set channel list using -l option \n"
"\twl dfs_channel_forced {-l <chanspec list> | 0}\n"
"\t20MHz : <channel>[/20]\n"
"\t40MHz : <channel>{{l|u}|/40}\n"
"\t80MHz : <channel>/80\n"
"\tChannels specified using '-l' option should be\n"
"seperated by ','/' ' and should be prefixed with '+'/'-'\n"
"Deletes existing configuration when '0' specified"},
{ "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 (range: -32 - 31)\n"
"\t-q quarter dBm units (range: -128 - 127)\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"},
{ "txpwrlimit", wl_get_txpwr_limit, WLC_CURRENT_PWR, -1,
"Return current tx power limit" },
{ "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 ]" },
{ "shmemx", wl_macregx, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set a shared memory location of PSMX:\n"
"\toffset [ value ] [band ]" },
{ "macregx", wl_macregx, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set any mac registers(include IHR and SB) of PSMX:\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)" },
{ "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" },
{ "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_macaddr, -1, WLC_SCB_AUTHORIZE,
"restrict traffic to 802.1X packets until 802.1X authorization succeeds" },
{ "deauthorize", wl_macaddr, -1, WLC_SCB_DEAUTHORIZE,
"do not restrict traffic to 802.1X packets until 802.1X authorization succeeds" },
{ "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"
},
{ "auth", wl_bsscfg_int, WLC_GET_AUTH, WLC_SET_AUTH,
"set/get 802.11 authentication type. 0 = OpenSystem, 1= SharedKey, 3=Open/Shared" },
{ "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"
"\t64 WPA2-802.1X/WPA2-Professional\n"
"\t128 WPA2-PSK/WPA2-Personal\n"
"\t0 disable WPA"
},
{ "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, type,"
"and channels are used.\n"
"All other values are silently discarded.\n"
SCAN_USAGE
},
#ifdef WLRCC
{ "roamscan_channels", wl_roamchannels, WLC_GET_VAR, -1,
"get roam channels\n"
},
#endif /* WLRCC */
{ "roam_prof", wl_roam_prof, WLC_GET_VAR, WLC_SET_VAR,
"get/set roaming profiles (need to specify band)\n"
"\tUsage: wl roam_prof_2g a|b|2g|5g flags rssi_upper rssi_lower delta, boost_thresh "
"boot_delta nfscan fullperiod initperiod backoff maxperiod\n"
},
{ "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
},
{ "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
},
{ "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\")" },
{ "status", wl_status, -1, -1,
"Print information about current network association.\n"
"\t(also known as \"assoc\")" },
{ "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" },
{ "chanspec_txpwr_max", wl_get_chanspec_txpwr_max, WLC_GET_VAR, -1,
"Return valid chanspecs with max tx power settings.\n"
"\t-b band (5(a) or 2(b/g))\n"
"\t-w bandwidth, 20, 40, 80, 160 or 8080\n"
},
{ "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."
},
{"txbf_rateset", wl_txbf_rateset, WLC_GET_TXBF_RATESET, WLC_SET_TXBF_RATESET,
TXBF_RATESET_USAGE
},
{ "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"},
{ "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"},
{ "ccode_info", wl_ccode_info, WLC_GET_VAR, -1,
"Get Country Code Info"},
{ "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)"},
{ "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."},
{ "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."},
{ "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)" },
{ "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)" },
{ "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" },
{ "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."},
{ "dfs_status", wl_dfs_status, WLC_GET_VAR, -1,
"Get dfs status"},
{ "dfs_status_all", wl_dfs_status_all, WLC_GET_VAR, -1,
"Get dfs status of multiple cores or parallel radar scans"},
{ "radar_status", wl_radar_status, WLC_GET_VAR, -1,
"Get radar detection status"},
{ "clear_radar_status", wl_clear_radar_status, -1, WLC_SET_VAR,
"Clear radar detection status"},
{ "radar_sc_status", wl_radar_sc_status, WLC_GET_VAR, WLC_SET_VAR,
"Get/clear sc radar detection status"},
{ "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"},
{ "arpoe", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/Disable arp agent offload feature"},
{ "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"},
{ "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)>"},
{ "pm_mute_tx", wl_pm_mute_tx, -1, WLC_SET_VAR,
"Sets parameters for power save mode with muted transmission path. Usage:\n"
"\twl pm_mute_tx 1 <deadline>\t: attempts to enable mode as soon as\n"
"\t\t\t timer of <deadline> (milliseconds) expires.\n"
"\twl pm_mute_tx 0\t: disables mode\n" },
{ "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"},
{ "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"
" Bit 7 - Auth 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"
" Bit 7 - Auth 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"},
{ "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_ext", wl_bitvecext, WLC_GET_VAR, WLC_SET_VAR,
"set/get bit arbitrary size hex filter bitmask for MAC" },
{ "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. \n"
"\t wl counters [options]. Options:\n"
"\t --nz : only non zero counters\n"
"\t --err : only error/warning related counters\n"
"\t --rx : only rx specific counters\n"
"\t --tx\n"
"\t --ctrl : only ctrl/mgmt related counters\n"
"\t --ucode : only ucode generated counters\n"
"\t --ucast\n"
"\t --mcast : only mcast+bcast related counters\n"
"\t --sec : security: only tkip/aes/ related counters\n"
"\t --ampdu --rx : combine options to narrow down selection\n"
"\t --ampdu --invert : use --invert to invert the selection"},
{ "subcounters", wl_subcounters, WLC_GET_VAR, WLC_SET_VAR,
"Return driver counter values of requested counters. \n"
"\t wl subcounters <version> <counters list> - To list requested counters\n"
"\t wl subcounters - To get the counters version FW is using\n"
"\t wl subcounters <version> - List supported counters in given version"},
{ "if_counters", wl_if_counters, WLC_GET_VAR, -1,
"Return driver counter values for current interface." },
{ "reset_cnts", wl_clear_counters, WLC_GET_VAR, -1,
"Clear driver counter values" },
{ "wlc_ver", wl_wlc_ver, WLC_GET_VAR, -1,
"returns wlc interface version" },
{ "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" },
{ "swdiv_stats", wl_swdiv_stats, WLC_GET_VAR, -1,
"Returns swdiv stats"},
{ "rxfifo_counters", wl_rxfifo_counters, WLC_GET_VAR, -1,
"Returns rxfifo counters"},
{ "assoc_info", wl_assoc_info, WLC_GET_VAR, -1,
"Returns the assoc req and resp information [STA only]" },
{ "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" },
{ "probresp_mac_filter", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get MAC filter based Probe response mode. \n"
"\t0 - Disable MAC filter based Probe response mode.\n"
"\t1 - Enable MAC filter based Probe response mode.\n"
"\tNo parameter - Returns the current setting."},
{ "eap_restrict", wl_bsscfg_int, WLC_GET_VAR, WLC_SET_VAR,
"set/get EAP restriction"},
{ "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"},
{ "overlay", wl_overlay, WLC_GET_VAR, WLC_SET_VAR,
"overlay virt_addr phy_addr size"},
{ "antgain", wl_antgain, WLC_GET_VAR, WLC_SET_VAR,
"Set temp ag0/1 value\n"
"usage: wl antgain ag0=0x1 ag1=0x2"
},
{ "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"
},
{ "txfifo_sz", wl_txfifo_sz, WLC_GET_VAR, WLC_SET_VAR,
"set/get the txfifo size; usage: wl txfifo_sz <fifonum> <size_in_bytes>" },
#if defined(linux)
{ "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
},
{ "wl_event_check", wl_event_check, -1, -1,
"listen and display brcm events\n"
"\tusage:wl -i <ifname> wl_event_check"},
#endif /* linux */
{ "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"
},
{ "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" },
{ "srchmem", wl_srchmem, WLC_GET_VAR, WLC_SET_VAR,
"g/set ucode srch engine memory"},
{"ptk_start", wl_ptk_start, -1, WLC_SET_VAR,
"Send specified \"wl_ptk_start \" params .\n"
"\tUsage: wl ptk_start <sec type> <awdl peer_addr> <pmk> <role>\n"},
#ifdef CLMDOWNLOAD
{ "clmload", wl_clmload, -1, WLC_SET_VAR,
"Download CLM data into a driver. Driver must be down.\n"
"\tUsage: wl clmload <clm blob file name>\n"
"\t Note obsolete syntax 'wl clmload 0/1 <clm blob file name>' is still accepted\n"
"\t but the download type 0/1 is no longer applicable and is ignored. Incremental\n"
"\t CLM download is no longer supported. Also reverting to the original built-in\n"
"\t CLM is no longer supported. (This syntax was 'wl clmload 0/1')"},
{ "txcapload", wl_txcapload, -1, WLC_SET_VAR,
"Download txcap data into a driver. Driver must be down.\n"
"\tUsage: wl txcapload <txcap file name>\n"},
{ "txcapver", wl_var_getandprintstr, WLC_GET_VAR, -1,
"List curent txcap downloaded information"},
{ "txcapconfig", wl_txcapctl, WLC_GET_VAR, WLC_SET_VAR,
"Set or get the txcap config setting for the low/high cap selection for each subband"},
{ "txcapstate", wl_txcapctl, WLC_GET_VAR, WLC_SET_VAR,
"Set or get the txcap state setting for the low/high cap selection for each subband"},
{ "txcapdump", wl_txcapdump, WLC_GET_VAR, -1,
"Get txcap dump information. Intended for design verification/debugging as opposed to\n"
"\tproduction usage and as such may change with little (or no) notice."},
#endif /* CLMDOWNLOAD */
{ "calload", wl_calload, -1, WLC_SET_VAR,
"Download CAL data into a driver. Driver must be down.\n"
"\tUsage: wl calload <cal file name> to download existing calibration data file\n"},
{ "caldump", wl_caldump, WLC_GET_VAR, -1,
"Dump calibration data and save it with calibration storage format.\n"
"\tUsage: wl caldump <cal file name> to dump current calibration info to file\n"},
{ "calload_chkver", wl_buf, -1, WLC_SET_VAR,
"Confirm TxCal load with provided version string"},
{ "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
{ "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)"
},
#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"
},
{ "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"},
{ "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>"},
{"txdelay_params", wl_txdelay_params, WLC_GET_VAR, -1,
"get chanim stats \n"
"\t Usage: wl txdelay_params ratio cnt period tune"
},
{"dlystats", wl_dlystats, WLC_GET_VAR, -1,
"dump delay statistics\n"
"\tUsage: wl dlystats [xx:xx:xx:xx:xx:xx] (optional mac address)\n"
"\tIf mac_addr is not specified, dump all of scbs"
},
{"dlystats_clear", wl_dlystats_clear, -1, WLC_SET_VAR,
"clear delay stats \n"
"\t Usage: wl dlystats_clear"
},
{"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>]"},
{ "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)"
},
#ifdef SERDOWNLOAD
{ "rwl_download", rwl_download, -1, WLC_SET_VAR,
"rwl_download <firmware> <nvram>\n"
"\trwl_download transfer firmware and nvram via remote interface\n"
},
{ "init", dhd_init, WLC_GET_VAR, WLC_SET_VAR,
"init <chip_id>\n"
"\tInitialize the chip.\n"
"\tCurrently only 4325, 4329, 43291, 4330a1 and 4330 (b1) are supported"
},
{ "download", dhd_download, WLC_GET_VAR, WLC_SET_VAR,
"download <binfile> <varsfile>\n"
"\tdownload file to dongle ram and start CPU\n"
"\tvars file will replace vars parsed from the CIS"
},
{ "upload", dhd_upload, WLC_GET_VAR, WLC_SET_VAR,
"upload <file> \n"
"\tupload the entire memory and save it to the file"
},
#endif /* SERDOWNLOAD */
{ "rpmt", wl_rpmt, -1, WLC_SET_VAR, "rpmt <pm1-to> <pm0-to>"},
{ "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" },
{ "mempool", wlu_mempool, WLC_GET_VAR, -1,
"Get memory pool statistics" },
{ "leaky_ap_stats", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/disable leaky ap stats collection and reporting\n"
"\t0 - disable\n"
"\t1 - enable" },
{ "leaky_ap_sep", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Enable/disable leaky ap to suppress the early packet before guard start time\n"
"\t0 - disable\n"
"\t1 - enable" },
#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_varint, WLC_GET_VAR, WLC_SET_VAR,
"Keep resource on"},
{ "sr_power_island", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Keep power islands on/off.\n"
"Usage: For get:wl sr_power_island\n"
" For set:wl sr_power_island 0x????\n"
" where ?-> 0 power_island off\n"
" ?-> 1 power_island on\n"
" eg: wl sr_power_island 0x1101"},
#endif /* SR_DEBUG */
{ "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 lpc_params <rate_stab_thresh> <pwr_stab_thresh>\n"
"\t\t<lpc_exp_time> <pwrup_slow_step>\n"
"\t\t<pwrup_fast_step> <pwrdn_slow_step>\n"},
{ "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"
},
{ "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_shrink <set> <[size]>\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"},
{ "event_log_get", wl_event_log_get, -1, WLC_SET_VAR,
"\t usage: wl event_log_get [-f <set_num>] [-g <set_num> -s <buf_size>]\n"
"\t -f: flush a log buffer of specified set being written to by trigerring logtrace\n"
"\t -g: get a log buffer of a specified set that is full but not delivered to host yet\n"
"\t Store the contents in a buffer of size specified with -s option\n"
"\t Note: Only -f option is currently supported\n"
},
{ "rmc_ar", wl_mcast_ar, WLC_GET_VAR, WLC_SET_VAR,
"Set active receiver to the one that matches the provided mac address\n"
"If there is no match among current RMC receivers, it will return fail\n"
"If mac address is set to all 0 (00:00:00:00:00:00), auto selection mode is enabled\n"
"and the transmitter will choose the active receiver automatically by RSSI\n"
"\tusage: wl rmc_ar [mac address]\n"
"Get the device mac that is set to be the active receiver for this transmitter\n"
"\tusage: wl rmc_ar\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 [enable|disable|counters|reset_cnts] | [<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},
{ "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]"},
{ "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]"},
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
{ "dump_modesw_dyn_bwsw", wl_dump_modesw_dyn_bwsw, WLC_GET_VAR, -1,
"Usage : wl dump_modesw_dyn_bwsw" },
#endif
{ "pwrstats", wl_pwrstats, WLC_GET_VAR, -1,
"Get power usage statistics\n"
"Usage: wl pwrstats [<type>] ..."},
{ "memuse", wl_memuse, WLC_GET_VAR, -1,
"Get memory usage statistics\n"
"Usage: wl memuse"},
{ "drift_stats_reset", wl_var_void, -1, WLC_SET_VAR,
"Reset drift statistics"},
{ "aibss_txfail_config", wl_aibss_txfail_config, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get txfail configuration for bcn_timeout, max tx retries and max atim failures\n"
"\tUsage: wl aibss_txfail_config [bcn_timeout max_retry max_atim_failure]"},
{ "ibss_route_tbl", wl_setiproute, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set ibss route table\n"
"\tUsage: wl ibss_route_tbl num_entries [{ip_addr1, mac_addr1}, ...]"},
{ "ip_route_table", wl_setiproute, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set ip route table\n"
"\tUsage: wl ip_route_tbl num_entries [{ip_addr1, mac_addr1}, ...]"},
{ "rsdb_mode", wl_bcm_config, WLC_GET_VAR, WLC_SET_VAR,
"Set/Get the RSDB mode. Possible values auto(-1), mimo(0), rsdb(1), 80p80(2)"},
{ "desired_bssid", wl_desired_bssid, WLC_GET_DESIRED_BSSID, WLC_SET_DESIRED_BSSID,
"Set or get the desired BSS ID value\n"
"\tUsage: wl desired_bssid [BSSID]"},
{ "ht_features", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"disable/enable/force proprietary 11n rates support. Interface must be down." },
{ "modesw_timecal", wl_modesw_timecal, WLC_GET_VAR, WLC_SET_VAR,
"Usage: \n\t wl modesw_timecal 0~1 for disable /enable \n"
"\t wl modesw_timecal to get Time statistics" },
{ "atim", wl_int, WLC_GET_ATIM, WLC_SET_ATIM,
"Set/Get the current ATIM window size" },
{ "pcie_bus_tput", wl_pcie_bus_throughput_params, WLC_GET_VAR, WLC_SET_VAR,
"Measure the pcie bus througput\n"
"Usage: wl pcie_bus_tput -n 64\n"},
{ "interface_create", wl_interface_create_action, -1, WLC_GET_VAR,
"create an AP/STA interface on a WLC instance that receives the IOVAR\n"
"\tUsage: wl interface_create ap/sta [MAC-address]\n"
"MAC-address: xx:xx:xx:xx:xx:xx"
},
{ "interface_remove", wl_interface_remove_action, -1, WLC_SET_VAR,
"Deletes the interface on which this command is received\n"
"\tUsage:\n\t wl interface_remove\n"
"\t wl -i <interface_name> interface_remove \n"
"\t wl interface_remove -C <bss_cfg_index> \n"
},
{"phy_txpwrcap_tbl", wl_phy_txpwrcap_tbl, WLC_GET_VAR, WLC_SET_VAR,
"Get the stored txpwr cap table:\n"
"\t\twl phy_txpwrcap_tbl\n"
"\tSet the txpwr cap table:\n"
"\t\twl phy_txpwrcap_tbl <Na0> <Na1> <Na2> <Na3> <Na4> <Na5> <Na6> <Na7>"
" <Cap_cell_ON> <Cap_cell_OFF>\n"
"\t\tNaX: Number of Antennas on Core X\n"
"\t\tCap_cell_ON: Pwr caps for valid antennas on all cores for Cell On status\n"
"\t\tCap_cell_OFF: Pwr caps for valid antennas on all cores for Cell Off status\n"
"\t\tPwr Caps are in qdBm, int8 format. Cap_cell_OFF values are optional.\n"},
{ "bcntrim_stats", wl_bcntrim_stats, WLC_GET_VAR, -1,
"Get Beacon Trim Statistics\n"
"\tUsage: wl bcntrim_stats\n"},
#ifdef ATE_BUILD
{ "gpaio", wl_gpaio, NULL, WLC_SET_VAR,
"Configure the GPAIO using different options as follows:\n\n"
"\tgpaio pmu_afeldo\n\n"
"\tgpaio pmu_txldo\n\n"
"\tgpaio pmu_vcoldo\n\n"
"\tgpaio pmu_lnaldo\n\n"
"\tgpaio pmu_adcldo\n\n"
"\tgpaio clear\n\n"},
#endif
{ "dfs_ap_move", wl_dfs_ap_move, WLC_GET_VAR, WLC_SET_VAR,
"Move the AP interface to dfs channel specified:\n"
"\t Default: Get the dfs scan status\n"
"\t -1: Abort recent AP move request (if in progress)\n"
"\t -2: Stunt recent AP move request (if in progress)\n"
"\t20MHz : [5g]<channel>[/20]\n"
"\t40MHz : [5g]<channel>/40[u,l]\n"
"\t80MHz : [5g]<channel>/80\n"
"\tchannel number (36-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 (36-224)\n"
"\t-w bandwidth 20 or 40\n"
"\t-s ctl sideband, -1=lower, 0=none, 1=upper"},
{"pmac", wl_macdbg_pmac, WLC_GET_VAR, -1,
"Get mac obj values such as of SHM and IHR\n"
"\tusage: wl pmac <type> <addresses up to 16> -s <step size>"
" -n <num> -b <bitmap> -w <write val> -r\n"
"<addresses> : a single address, or multiple, upto 16 addresses, in Decimal or Hex\n"
"<step size> = 0 or 2 or 4\n"
"<num> : how many to print\n"
"<bitmap> : 32-bit value\n"
"<w_val> : write value to the registers\n"
"-r option is used to specify internal address:"},
{ "vasip_counters_clear", wl_var_void, -1, WLC_SET_VAR,
"clear vasip counters"},
{"svmp_mem", wl_svmp_mem, WLC_GET_VAR, WLC_SET_VAR,
"Usage: wl svmp_mem <offset> <len> [ <val> ]\n"
"With 2 params, read svmp memory at offset for len of 16-bit width.\n"
"With 3rd param, set the same range to the given value\n"},
{"mu_rate", wl_mu_rate, WLC_GET_VAR, WLC_SET_VAR,
"Force the tranmission rate for each user, rate0 is for user0; rate1 is for user1...\n"
"Usage: wl mu_rate { [auto | -1] | [[rate0] [rate1] [rate2] [rate3]]\n"
"no input: read current MU-MIMO rate.\n"
"auto or -1: turn on auto rate.\n"},
{"mu_group", wl_mu_group, WLC_GET_VAR, WLC_SET_VAR,
"Force the group recommendation result or set parameters for VASIP group recomendation\n"
"\tUsage: no parameters means getting configs\n"
"\t 'wl mu_group [-g P0 [P01 P02 ... P04] [... -g Px [Px1 Px2 ... Px4]]] [-f F]'\n"
"\t 'wl mu_group [-g -1] [-m M] [-n N]'\n"
"\t Combination of '-g 0 XXX' with '-m M' or '-n N' is invalid\n"
"\t Example1: wl mu_group -g 0 0x016 0x209 0x309 -g 1 0x009 0x217 -g 2 0x115 0x308\n"
"\t Example2: wl mu_group -g 0 0x007 0x109 0x209 0x308 -f 0\n"
"\t Example3: wl mu_group -g -1\n"
"\t Example4: wl mu_group -g -1 -m 1 -n 4\n"
"\t Example5: wl mu_group -m 1 -n 4 (only valid under auto grouping)\n"
"\t -g: Force group recommendation (x<=7, up to 8 options)\n"
"\t P0=-1 means VASIP group recommendation (not-forced mode, default)\n"
"\t P0~Px are expected to be 0~x in forced mode\n"
"\t Pxy: three nibbles for (user_id<<8 + (user_nss-1)<<4 + user_mcs)\n"
"\t -f: Force MCS and only valid with '-g 0 XXX'\n"
"\t F=0: auto MCS from VASIP MCS recommendation\n"
"\t F=1: forced MCS according to '-g' argument (default when froced grouping)\n"
"\t -m: Method for VASIP group recommendation, M>=0\n"
"\t M=0: old method: 1 group for all admitted users with GID=9\n"
"\t M>0: new method: M=1 for N best THPT groups\n"
"\t -n: Number of groups reported to MAC for VASIP group recommendation, N=1~15"},
{"mu_policy", wl_mu_policy, WLC_GET_VAR, WLC_SET_VAR,
"Configure the MU admission control policies\n"
"\tUsage: no parameters means getting configs\n"
"\t 'wl mu_policy [-sched_timer T] [-pfmon P] [-pfmon_gpos G] [-samebw B]"
" [-nrx R] [-max_muclients C]'\n"
"\t Example1: wl mu_policy -sched_timer 60 -pfmon 1 -pfmon_gpos 0 -samebw 0 -nrx 1\n"
"\t Example2: wl mu_policy -sched_timer 0\n"
"\t Example3: wl mu_policy -pfmon 0\n"
"\t Example4: wl mu_policy -nrx 2\n"
"\t Example5: wl mu_policy -max_muclients 4\n"
"\t -sched_timer: Configure the timer interval for the score based MU client scheduler\n"
"\t T=0 means the scheduler is disabled\n"
"\t T>0 means the timer duration in seconds (default 60)\n"
"\t -pfmon: Configure the perfomance monitors (mutxcnt and gpos)'\n"
"\t P=0: Disable the perfomance monitors\n"
"\t P=1: Enable the perfomance monitors and black lists\n"
"\t -pfmon_gpos: Configure the gpos performance monitor\n"
"\t G=0: Disable the gpos performance monitor\n"
"\t G=1: Enable the gpos performance monitor\n"
"\t -samebw: Configure the BW check at admission control\n"
"\t B=0: Allow clients with different BW to be admitted\n"
"\t B=1: Only clients with the same BW can be admitted\n"
"\t -nrx: Configure the max nrx (number of RX streams) of the clients\n"
"\t R=1: Only 1x1 MU STAs can be admitted\n"
"\t R=2: Both 1x1 and 2x2 MU STAs can be admitted\n"
"\t -max_muclients: Configure the max number of clients\n"
"\t C: Can be a value between 2~8"},
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
{ "svmp_sampcol", wl_svmp_sampcol, WLC_GET_VAR, WLC_SET_VAR,
"No parameters means getting configs\n"
"\tOptional parameters for BCM4365 are:\n"
"\t-e enable capture, 0: enable, 1: disable,"
"without this field means setting configs only\n"
"\t-t trigger mode, 0: pkt-proc transition trigger (need 2 additional states setting),"
"1: force immediate trigger, 2: radar-det trigger\n"
"\t-w waiting counter in sample (default: 0)\n"
"\t-l capture length in sample (default: 128)\n"
"\t-s source select, set phy1_mux and rx1_mux\n"
"\t\tphy1_mux, 0: gpioOut, 1: fftOut, 2: dbgHx, 3: rx1mux\n"
"\t\trx1_mux, 4: farrowOut, 6: dcFilterOut, 7: rxFilterOut, 8: aciFilterOut\n"
"\t-d dual capture mode, use ACPHY sample_collect HW (default: off)\n"
"\t\tdata_mux, 4: farrowOut, 5: iqCompOut, 6: dcFilterOut, 7: rxFilterOut\n"
"\t-r samplerate, 0: 1xBW, 1: 2xBW (only farrowOut supports 2xBW)\n"
"\t-p memory packing selection, set packing_mode, packing_order, packing_format,"
"single-core selection (default 1 0 0 0)\n"
"\t\tpacking_mode, 0: for dual capture, 1: 4-core, 2: first 2-core, 3: single-core\n"
"\t\tpacking_order, 0: big endian, 1: little endian\n"
"\t\tpacking_format, 0: IQswap of cfix, 1: VASIP cfix format,"
"4365A0/B0 only supports IQswap of cfix\n"
"\t\tsingle-core selection, 0~3 (4365A0/B0 only forces to core0)\n"
"\t-a SVMP buffer address in word size, set addr_start and addr_end"
"(default 0x25800 0x26800)\n"
"\t-i send interrupt to VASIP when capture done (default 1)\n"},
#endif
{ "scanmac", wl_scanmac, WLC_GET_VAR, WLC_SET_VAR,
"Configure scan MAC using subcommands:\n"
"\tscanmac enable <0|1>\n"
"\tscanmac bsscfg\n"
"\tscanmac config <mac> <random_mask> <scan_bitmap>\n"},
{"mkeep_alive", wl_mkeep_alive, WLC_GET_VAR, WLC_SET_VAR,
"Send specified \"mkeep-alive\" packet periodically.\n"
"Usage: wl mkeep_alive <index0-7> [[<immediate>] <period> <packet>]\n"
"\tindex: 0 - 7 (for set or get).\n"
"\timmediate: Send first packet immediately.\n"
"\tperiod: Re-transmission period in msecs, 0 to disable.\n"
"\tpacket: Hex packet contents to transmit. The packet contents should\n"
"\t include the entire ethernet packet (ethernet header, IP header, UDP\n"
"\t header, and UDP payload) specified in network byte order. If no\n"
"\t packet is specified, a nulldata frame will be sent instead.\n\n"
"E.g. Send keep alive packet every 30 seconds using id-1:\n"
"\twl mkeep_alive 1 30000 0x0014a54b164f000f66f45b7e08004500001e000040004011c"
"52a0a8830700a88302513c413c4000a00000a0d\n" },
{ "tcmstbl", wl_get_tcmstbl_entry, WLC_GET_VAR, -1,
"Returns the total power in hdBm and the Tx core Mask for the combination: \n"
"\t-c core number (0-7) default 0\n"
"\t-s cell status (0 or 1) default 0\n"
"\t-a antenna map (each nibble holds one core, core0 LSB) default 0x0 \n"},
{ "dfs_max_safe_tx", wl_dfs_max_safe_tx, WLC_GET_VAR, WLC_SET_VAR,
"Thresholds for tx traffic on main core. On crossing this threshold, scan core \n"
"background DFS scan results may be discarded depending on dfs_txblank_check_mode.\n"
"\t Default: Get the dfs max safe tx thresholds for non-adjacent (and adjacent) cases.\n"},
{ "dfs_txblank_check_mode", wl_varint, WLC_GET_VAR, WLC_SET_VAR,
"Control if tx on main core must be considered at end of background CAC on scan core.\n"
"\t Default: Get the dfs txblank check mode.\n"},
{ "nd_ra_limit_intv", wl_nd_ra_limit_intv, WLC_GET_VAR, WLC_SET_VAR,
"Get / Set IPv6 RA rate limit interval\n"
"\tUsage(Set): nd_ra_limit_intv -t <type> -p <percentage(<100)> -m <fixed time>\n"
"\t : nd_ra_limit_intv -t 0 -p XX -m YYY\n"
"\t : nd_ra_limit_intv -t 1 -m ZZZ\n"
"\tUsage(Get): nd_ra_limit_intv\n"
"\t<type> 0: percentage of lifetime, 1: fixed time\n"},
{"sim_PM", wl_sim_pm, WLC_GET_VAR, WLC_SET_VAR,
"Set the fw into a simulated PM in idle associated mode\n"
"\t[0 / 1] - Disable / Enable\n\t-c - Cycle time in TUs (max 10,000)\n\t-u Up time in TUs "
"(max 1000)\n"},
{ "wowl_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"},
{ "hc", wl_hc, WLC_GET_VAR, WLC_SET_VAR,
"Health Check command group\n"
"\tusage: wl hc tx <attribute> [value]\n"
"\tusage: wl hc rx <attribute> [value]\n"
"\tGet or Set tx/rx attribute values"},
{"fbt_r0kh_id", wl_varstr, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set R0 Key Holder Idenitifer for an interface\n"
"\tUsage: wl fbt_r0kh_id <string>\n"
"String: Maximum 48 byte R0 Key Holder ID\n"},
{"fbt_r1kh_id", wl_iov_mac, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set 802.11r R1 Key Holder Idenitifer for an interface\n"
"\tUsage: wl fbt_r1kh_id <mac-address>\n"
"MAC-address: xx:xx:xx:xx:xx:xx\n"},
{"fbt_auth_resp", wl_varstr, WLC_GET_VAR, WLC_SET_VAR,
"Get/Set fbt auth response for an interface\n"
"\tUsage: wl fbt_auth_resp <string>\n"
"String: Maximum 48 byte FBT auth response\n"},
{ "icmp_hostip", wl_hostip, WLC_GET_VAR, WLC_SET_VAR,
"Add a host-ip address or display current address set"},
{ "icmp_hostipv6", wl_hostipv6_extended, WLC_GET_VAR, WLC_SET_VAR,
"Set host-ipv6 address or display current addresses set for icmp offloads\n"
"\tUse NULL ipv6 address - :: to flush all addresses\n"
"Usage:\n\tSET: wl icmp_hostipv6 <ipv6addr1> <ipv6addr2>\n"
"\tGET: wl icmp_hostipv6"},
{"wake_timer", wl_wake_timer, WLC_GET_VAR, WLC_SET_VAR,
"\tSend wake event to host periodically\n"
"\tUsage : wl wake_timer <period> <limit>\n"
"\t<period>: value in ms\n"
"\t<limit>: value to specify num of events\n"
"\t\t 0 : disable\n"
"\t\t 0xFFFF : Infinite\n"
"\t\t non-zero value: num of events\n"},
{ "idauth", wl_idauth, WLC_GET_VAR, WLC_SET_VAR,
"IDAUTH command group\n"
"\tusage: wl idauth config -a 0/1 -b 1000 -c 3 -e 1000 -g 1000 -m 10\n"
"\t\t-a: authentication offload enable/disable \n"
"\t\t-c: EAPOL retry count\n"
"\t\t-e: EAPOL retry Interval\n"
"\t\t-g: GTK rotation interval\n"
"\t\t-m: MIC fail count for blacklist\n"
"\t\t-b: Blacklist age\n"
"\tusage: wl idauth peer_info\n"
"\tusage: wl idauth counters\n"},
{ "utrace_capture", wl_utrace_capture, WLC_GET_VAR, -1,
"Read the template RAM for ucode trace data.\n"},
{ "wds_ap_ifname", wl_wds_ap_ifname, WLC_GET_VAR, -1,
"Get associated AP interface name for WDS interface."},
{ "netx_ping", wl_hostip, -1, WLC_SET_VAR,
"Add a host-ip address or display them"},
{ "netx_ifconfig", wl_netx_ifconfig, WLC_GET_VAR, WLC_SET_VAR,
"Configure the Netx interface, or display current settings\n"
"\tUsage: wl netx_ifconfig [<ip> <netmask> <gateway>]\n"},
#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 */
{ 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 */
static 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)"
};
#define MAX_MODULES 256
static cmd_t* module_cmds[MAX_MODULES];
static int module_count = 0;
/* register commands for a module */
void
wl_module_cmds_register(cmd_t *cmds)
{
if (cmds == NULL)
return;
if (module_count < MAX_MODULES) {
module_cmds[module_count] = cmds;
module_count++;
}
else
fprintf(stderr, "err - module count over %d\n", MAX_MODULES);
}
/* common function to find a command */
cmd_t *
wlu_find_cmd(char *name)
{
int i;
cmd_t *cmd = NULL;
/* search cmd in modules */
for (i = 0; i < module_count; i++) {
/* search cmd in one cmd table */
for (cmd = module_cmds[i]; cmd->name; cmd++) {
/* stop if we find a matching name */
if (!strcmp(cmd->name, name)) {
break;
}
}
/* if a match was found, break out of module loop */
if (cmd->name != NULL) {
break;
}
}
return (cmd->name != NULL) ? cmd : NULL;
}
/* return global ioctl_version */
int
wl_get_ioctl_version(void)
{
return ioctl_version;
}
/* return the address of bufstruct_wlu, global buf */
char *
wl_get_buf(void)
{
return buf;
}
/* 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();
/* register general wl commands */
wl_module_cmds_register(wl_cmds);
/* add wluc module init here */
wluc_phy_module_init();
wluc_wnm_module_init();
wluc_cac_module_init();
wluc_rmc_module_init();
wluc_rrm_module_init();
wluc_wowl_module_init();
wluc_pkt_filter_module_init();
wluc_mfp_module_init();
wluc_ota_module_init();
wluc_bssload_module_init();
wluc_stf_module_init();
wluc_offloads_module_init();
wluc_tpc_module_init();
wluc_toe_module_init();
wluc_arpoe_module_init();
wluc_keep_alive_module_init();
wluc_ap_module_init();
wluc_ampdu_module_init();
wluc_ampdu_cmn_module_init();
wluc_bmac_module_init();
wluc_ht_module_init();
wluc_wds_module_init();
wluc_keymgmt_module_init();
wluc_scan_module_init();
wluc_obss_module_init();
wluc_prot_obss_module_init();
wluc_lq_module_init();
wluc_seq_cmds_module_init();
wluc_btcx_module_init();
wluc_led_module_init();
wluc_interfere_module_init();
wluc_ltecx_module_init();
#ifdef WLEXTLOG
wluc_extlog_module_init();
#endif /* WLEXTLOG */
#ifdef WL_NAN
wluc_nan_module_init();
#endif /* WL_NAN */
#ifdef WLNDOE
wluc_ndoe_module_init();
#endif /* WLNDOE */
#ifdef WLPFN
wluc_pfn_module_init();
#endif /* WLPFN */
#ifdef BT_WIFI_HANDOVER
wluc_tbow_module_init();
#endif /* BT_WIFI_HANDOVER */
#ifdef WLP2P
wluc_p2p_module_init();
#endif /* WLP2PO */
#ifdef WLTDLS
wluc_tdls_module_init();
#endif /* WLTDLS */
#ifdef TRAFFIC_MGMT
wluc_trf_mgmt_module_init();
#endif /* TRAFFIC_MGMT */
#ifdef WL_PROXDETECT
wluc_proxd_module_init();
#endif /* WL_PROXDETECT */
#ifdef WLP2PO
wluc_p2po_module_init();
#endif /* WLP2PO */
#ifdef WLANQPO
wluc_anqpo_module_init();
#endif /* WLANQPO */
#ifdef WL_BTCDYN
wluc_btcdyn_module_init();
#endif /* WL_BTCDYN */
#ifdef WLMESH
wluc_mesh_module_init();
#endif /* WLMESH */
#ifdef WL_MSCH
wluc_msch_module_init();
#endif /* WL_MSCH */
#ifdef WLBDO
wluc_bdo_module_init();
#endif /* WLBDO */
#ifdef WL_RANDMAC
wluc_randmac_module_init();
#endif /* WL_RANDMAC */
#ifdef WLTKO
wluc_tko_module_init();
#endif /* WLTKO */
#ifdef WL_NATOE
wluc_natoe_module_init();
#endif /* WL_NATOE */
#ifdef WLRSDB
wluc_rsdb_module_init();
#endif
wluc_he_module_init();
#ifdef WL_MBO
wluc_mbo_module_init();
#endif /* WL_MBO */
#ifdef ECOUNTERS
wluc_ecounters_module_init();
#endif
#ifdef HOFFLOAD_MODULES
wluc_hoffload_module_init();
#endif /* HOFFLOAD_MODULES */
}
int wlc_ver_major(void *wl)
{
int err;
wl_wlc_version_t wlc_ver;
memset(&wlc_ver, 0, sizeof(wlc_ver));
err = wlu_iovar_get(wl, "wlc_ver", &wlc_ver, sizeof(wl_wlc_version_t));
if (err != BCME_OK) {
printf("Unable to get wlc_ver iovar %d\n", err);
/* old firmware may not support it */
return 0;
}
return wlc_ver.wlc_ver_major;
}
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;
}
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);
nrows = 0;
for (i = 0; i < module_count; i++) {
for (cmd = module_cmds[i]; 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 (i = 0; i < module_count; i++) {
for (cmd = module_cmds[i]; 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;
int i;
/* 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 (i = 0; i < module_count; i++) {
for (cmd = module_cmds[i]; 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.
*/
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;
}
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;
}
int wl_buf(void *wl, cmd_t *cmd, char **argv)
{
char* data;
int err;
int len;
int i;
strcpy(buf, cmd->name);
/* 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_SMLEN); 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_SMLEN))) {
len = dtoh32(*(int *)buf);
if (len > WLC_IOCTL_SMLEN) {
data = buf + sizeof(int);
for (i = 0; i < len; i ++)
printf("%02x", (uint8)(data[i]));
printf("\n");
} else {
err = BCME_BUFTOOLONG;
}
}
return err;
}
/* 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
*/
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
*/
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
*/
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
*/
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, char *iovar, uint16 flag, 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) | flag;
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, iovar,
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, "generic_dload", 0, 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;
}
int
dload_blob(void *wl, char *iovar, unsigned char *org_buf, uint32 datalen)
{
int chunk_len, cumulative_len = 0;
int size2alloc;
unsigned char *new_buf;
int err = 0, data_offset;
uint16 dl_flag = DL_BEGIN;
data_offset = OFFSETOF(wl_dload_data_t, data);
size2alloc = data_offset + MAX_CHUNK_LEN;
if ((new_buf = (unsigned char *)malloc(size2alloc)) != NULL) {
memset(new_buf, 0, size2alloc);
do {
if (datalen >= MAX_CHUNK_LEN)
chunk_len = MAX_CHUNK_LEN;
else
chunk_len = datalen;
memcpy(new_buf + data_offset, org_buf + cumulative_len, chunk_len);
cumulative_len += chunk_len;
if (datalen - chunk_len == 0)
dl_flag |= DL_END;
err = download2dongle(wl, iovar, dl_flag, DL_TYPE_CLM,
new_buf, data_offset + chunk_len);
dl_flag &= ~DL_BEGIN;
datalen = datalen - chunk_len;
} while ((datalen > 0) && (err == 0));
free(new_buf);
} else {
err = BCME_NOMEM;
}
return err;
}
int
dload_clm_blob(void *wl, unsigned char *org_buf, uint32 datalen)
{
int chunk_len, cumulative_len = 0;
int size2alloc;
unsigned char *new_buf;
int err = 0, data_offset;
uint16 dl_flag = DL_BEGIN;
data_offset = OFFSETOF(wl_dload_data_t, data);
size2alloc = data_offset + MAX_CHUNK_LEN;
if ((new_buf = (unsigned char *)malloc(size2alloc)) != NULL) {
memset(new_buf, 0, size2alloc);
do {
if (datalen >= MAX_CHUNK_LEN)
chunk_len = MAX_CHUNK_LEN;
else
chunk_len = datalen;
memcpy(new_buf + data_offset, org_buf + cumulative_len, chunk_len);
cumulative_len += chunk_len;
if (datalen - chunk_len == 0)
dl_flag |= DL_END;
err = download2dongle(wl, "clmload", dl_flag, DL_TYPE_CLM,
new_buf, data_offset + chunk_len);
dl_flag &= ~DL_BEGIN;
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;
unsigned long status = 0;
unsigned char *new_buf = NULL;
uint32 clm_data_len;
unsigned char *new_ptr;
int filelen = 0;
const char clm_magic_string[] = {'C', 'L', 'M', ' ', 'D', 'A', 'T', 'A'};
const char blob_magic_string[] = {'B', 'L', 'O', 'B'};
/* 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;
}
/* fstat is a linux/unix, not a stdio function. This is replaced everywhere with function
* fsize() which calls fseek(fp, 0L, SEEK_END), ftell(fp), fseek(fp, 0L, SEEK_SET) sequence
* to determine the file size.
*/
if ((filelen = fsize(fp)) < 0) {
fprintf(stderr, "fseek or ftell on input file %s return error %s\n",
clmfn, strerror(errno));
ret = BCME_ERROR;
goto error;
}
clm_filelen = (uint)filelen;
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 a CLM binary file or CLM blob file. The CLM binary file has
* been obsoleted in favor of the "blob" format that allows additional data
* to included in a modular/independent way. The blob format is downloaded
* via the new 'clmload' iovar. The binary format uses the legacy and now obsolete
* 'generic_dload' iovar.
*
* Newer driver builds no longer support the generic_dload iovar but this wl
* command utility will support either format/iovar combination based on the
* download file's magic string for some transition time.
*/
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;
}
/* CLM blob or binary format file? */
if (memcmp(new_buf, blob_magic_string, sizeof(blob_magic_string)) == 0) {
/* CLM blob file? They start with magic string 'BLOB' */
printf("Downloading CLM blob format file %s\n", clmfn);
ret = dload_clm_blob(wl, new_buf, clm_filelen);
} 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;
printf("Downloading legacy, obsolete CLM binary format file %s as a %s CLM\n",
clmfn, ds_id ? "incremental":"base");
ret = dload_clm(wl, clm_data_len, new_ptr, ds_id);
} else {
fprintf(stderr, "input file is missing CLM binary or CLM blob magic string\n");
ret = -1;
goto error;
}
error:
if (new_buf)
free(new_buf);
if (fp)
fclose(fp);
return ret;
}
#define TXCAP_INPUT_FILE_MIN_LEN 32
int
process_txcap_data(void *wl, char *txcapfn)
{
int ret = 0;
FILE *fp = NULL;
unsigned int txcap_filelen;
unsigned long status = 0;
unsigned char *new_buf = NULL;
int filelen = 0;
/* Open the txcap download file */
if (!(fp = fopen(txcapfn, "rb"))) {
fprintf(stderr, "unable to open input file %s\n", txcapfn);
ret = -EINVAL;
goto error;
}
/* fstat is a linux/unix, not a stdio function. This is replaced everywhere with function
* fsize() which calls fseek(fp, 0L, SEEK_END), ftell(fp), fseek(fp, 0L, SEEK_SET) sequence
* to determine the file size.
*/
if ((filelen = fsize(fp)) < 0) {
fprintf(stderr, "fseek or ftell on input file %s return error %s\n",
txcapfn, strerror(errno));
ret = -EINVAL;
goto error;
}
txcap_filelen = (uint)filelen;
if (txcap_filelen == 0) {
fprintf(stderr, "input file %s is empty (i.e. zero length)\n", txcapfn);
ret = -EINVAL;
goto error;
}
if ((new_buf = malloc(txcap_filelen)) == NULL) {
fprintf(stderr, "unable to allocate %u bytes based on input file size!\n",
txcap_filelen);
ret = -ENOMEM;
goto error;
}
status = fread(new_buf, 1, txcap_filelen, fp);
if (status != txcap_filelen) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
txcapfn, txcap_filelen);
ret = -EINVAL;
goto error;
}
/* Basic sanity check on size. Make sure there is enough for any magic string plus
* a little more for good measure.
*/
if (status < TXCAP_INPUT_FILE_MIN_LEN) {
fprintf(stderr, "size of input file %s is less than %d bytes."
" This can't be a txcap file!\n", txcapfn, TXCAP_INPUT_FILE_MIN_LEN);
ret = -EINVAL;
goto error;
} else if (status != txcap_filelen) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
txcapfn, txcap_filelen);
ret = -EINVAL;
goto error;
}
/* txcap package? */
if (memcmp(new_buf, blob_magic_string, sizeof(blob_magic_string)) == 0) {
/* MSF packaged file? They start with magic string 'BLOB' */
printf("Downloading txcap package format file %s\n", txcapfn);
ret = dload_blob(wl, "txcapload", new_buf, txcap_filelen);
} else {
fprintf(stderr, "input file is missing txcap package magic string\n");
ret = -1;
goto error;
}
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;
char *arg1;
char *arg2;
char* fname;
BCM_REFERENCE(cmd);
/* argv is pointing at the clmload command name to start */
if ((arg1 = *++argv) == NULL) {
fprintf(stderr, "too few arguments (none)\n");
return BCME_USAGE_ERROR;
} else if ((arg2 = *++argv) == NULL) {
/* one argument - use it, arg1, as the file name */
fname = arg1;
} else if ((*++argv) == NULL) {
/* two arguments - use the last, arg2, as the filename
* while ignoring the first argument. We no longer
* support downloading anything but a base CLM,
*/
fname = arg2;
} else {
fprintf(stderr, "too mang arguments (3 or more)\n");
return BCME_USAGE_ERROR;
}
ret = process_clm_data(wl, fname, 0);
return ret;
}
static int
wl_txcapload(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
char *arg1;
char* fname;
BCM_REFERENCE(cmd);
/* argv is pointing at the clmload command name to start */
if ((arg1 = *++argv) == NULL) {
/* too few arguments (none) */
return -1;
} else if (*++argv == NULL) {
/* one argument - use it, arg1, as the file name */
fname = arg1;
} else {
/* too mang arguments (2 or more) */
return -1;
}
ret = process_txcap_data(wl, fname);
return ret;
}
static int
wl_txcapctl(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
void *ptr = NULL;
wl_txpwrcap_ctl_t *txcap_ctl_ptr;
wl_txpwrcap_ctl_t txcap_ctl;
int i;
if (!(argv[1])) { /* Get */
if ((err = wlu_var_getbuf_med(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
txcap_ctl_ptr = ptr;
printf("%d %d %d %d %d\n",
txcap_ctl_ptr->ctl[0],
txcap_ctl_ptr->ctl[1],
txcap_ctl_ptr->ctl[2],
txcap_ctl_ptr->ctl[3],
txcap_ctl_ptr->ctl[4]);
} else { /* Set */
for (i = 0; i < 5; i++) {
if (*++argv) {
txcap_ctl.ctl[i] = strtol(*argv, NULL, 0);
} else {
return BCME_USAGE_ERROR;
}
}
if (argv[1]) {
return BCME_USAGE_ERROR;
}
else {
(void)txcap_ctl;
txcap_ctl.version = TXPWRCAPCTL_VERSION;
if ((err = wlu_var_setbuf(wl, cmd->name, &txcap_ctl, sizeof(txcap_ctl)))
< 0)
return BCME_ERROR;
}
}
return err;
}
static int
wl_txcapdump(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
void *ptr = NULL;
uint8 txcap_dump_version;
int i;
if (!(argv[1])) { /* Get */
if ((err = wlu_var_getbuf_med(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
txcap_dump_version = *((uint8 *)ptr);
if (txcap_dump_version == 2) {
wl_txpwrcap_dump_t *txcap_dump_ptr = ptr;
printf("current country %c%c, channel(control) %d\n",
txcap_dump_ptr->current_country[0],
txcap_dump_ptr->current_country[1],
txcap_dump_ptr->current_channel);
printf("low/high cap config: ");
for (i = 0; i < TXPWRCAP_NUM_SUBBANDS; i++) {
printf(" %d", txcap_dump_ptr->config[i]);
}
printf("\n");
printf("low/high cap state: ");
for (i = 0; i < TXPWRCAP_NUM_SUBBANDS; i++) {
printf(" %d", txcap_dump_ptr->state[i]);
}
printf("\n");
printf("high cap state enabled %d\n",
txcap_dump_ptr->high_cap_state_enabled);
printf("wci2 cell status last %d\n",
txcap_dump_ptr->wci2_cell_status_last);
if (!txcap_dump_ptr->download_present) {
printf("txcap download not present\n");
} else {
printf("subbands %d, antennas %d, antennas per core",
txcap_dump_ptr->num_subbands, txcap_dump_ptr->num_antennas);
for (i = 0; i < TXPWRCAP_MAX_NUM_CORES; i++) {
printf(" %d", txcap_dump_ptr->num_antennas_per_core[i]);
}
printf(", cc groups %d\n", txcap_dump_ptr->num_cc_groups);
printf("current cc group info index %d\n",
txcap_dump_ptr->current_country_cc_group_info_index);
printf(" tx caps (decimal qdbm, per subband, per antenna,"
" e.g. sb0_a0, sb0_a1, sb1_a0, sb1_a1, ...)\n");
printf(" low cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ", txcap_dump_ptr->low_cap[i]);
}
printf("\n");
printf(" high cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ", txcap_dump_ptr->high_cap[i]);
}
printf("\n");
}
} else if (txcap_dump_version == 3) {
wl_txpwrcap_dump_v3_t *txcap_dump_ptr = ptr;
printf("current country %c%c, channel(control) %d\n",
txcap_dump_ptr->current_country[0],
txcap_dump_ptr->current_country[1],
txcap_dump_ptr->current_channel);
printf("low/high cap config: ");
for (i = 0; i < TXPWRCAP_NUM_SUBBANDS; i++) {
printf(" %d", txcap_dump_ptr->config[i]);
}
printf("\n");
printf("low/high cap state: ");
for (i = 0; i < TXPWRCAP_NUM_SUBBANDS; i++) {
printf(" %d", txcap_dump_ptr->state[i]);
}
printf("\n");
printf("high cap state enabled %d\n",
txcap_dump_ptr->high_cap_state_enabled);
printf("wci2 cell status last %d\n",
txcap_dump_ptr->wci2_cell_status_last);
if (!txcap_dump_ptr->download_present) {
printf("txcap download not present\n");
} else {
printf("subbands %d, antennas %d, antennas per core",
txcap_dump_ptr->num_subbands, txcap_dump_ptr->num_antennas);
for (i = 0; i < TXPWRCAP_MAX_NUM_CORES; i++) {
printf(" %d", txcap_dump_ptr->num_antennas_per_core[i]);
}
printf(", cc groups %d\n", txcap_dump_ptr->num_cc_groups);
printf("current cc group info index %d\n",
txcap_dump_ptr->current_country_cc_group_info_index);
printf(" tx caps (decimal qdbm, per subband, per antenna,"
" e.g. sb0_a0, sb0_a1, sb1_a0, sb1_a1, ...)\n");
if (txcap_dump_ptr->cap_states_per_cc_group == 2) {
printf(" low cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_low_wci2_low_cap[i]);
}
printf("\n");
printf(" high cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_low_wci2_high_cap[i]);
}
printf("\n");
} else {
printf(" host low wci2 low cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_low_wci2_low_cap[i]);
}
printf("\n");
printf(" host low wci2 high cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_low_wci2_high_cap[i]);
}
printf("\n");
printf(" host high wci2 low cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_high_wci2_low_cap[i]);
}
printf("\n");
printf(" host high wci2 high cap: ");
for (i = 0;
i < txcap_dump_ptr->num_subbands *
txcap_dump_ptr->num_antennas;
i++) {
printf("%4d ",
txcap_dump_ptr->host_high_wci2_high_cap[i]);
}
printf("\n");
}
}
} else {
err = BCME_USAGE_ERROR;
}
} else { /* Set */
err = BCME_USAGE_ERROR;
}
return err;
}
#endif /* CLMDOWNLOAD */
#define CAL_INPUT_FILE_MIN_LEN 32
int
process_cal_data(void *wl, char *calfn)
{
int ret = 0;
FILE *fp = NULL;
unsigned int cal_filelen;
unsigned long status = 0;
unsigned char *new_buf = NULL;
int filelen = 0;
/* Open the CAL download file */
if (!(fp = fopen(calfn, "rb"))) {
fprintf(stderr, "unable to open input file %s\n", calfn);
ret = -EINVAL;
goto error;
}
/* fstat is a linux/unix, not a stdio function. This is replaced everywhere with function
* fsize() which calls fseek(fp, 0L, SEEK_END), ftell(fp), fseek(fp, 0L, SEEK_SET) sequence
* to determine the file size.
*/
if ((filelen = fsize(fp)) < 0) {
fprintf(stderr, "fseek or ftell on input file %s return error %s\n",
calfn, strerror(errno));
ret = -EINVAL;
goto error;
}
cal_filelen = (uint)filelen;
if (cal_filelen == 0) {
fprintf(stderr, "input file %s is empty (i.e. zero length)\n", calfn);
ret = -EINVAL;
goto error;
}
if ((new_buf = malloc(cal_filelen)) == NULL) {
fprintf(stderr, "unable to allocate %u bytes based on input file size!\n",
cal_filelen);
ret = -ENOMEM;
goto error;
}
status = fread(new_buf, 1, cal_filelen, fp);
if (status != cal_filelen) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
calfn, cal_filelen);
ret = -EINVAL;
goto error;
}
/* Basic sanity check on size. Make sure there is enough for any magic string plus
* a little more for good measure.
*/
if (status < CAL_INPUT_FILE_MIN_LEN) {
fprintf(stderr, "size of input file %s is less than %d bytes."
" This can't be a cal file!\n", calfn, CAL_INPUT_FILE_MIN_LEN);
ret = -EINVAL;
goto error;
} else if (status != cal_filelen) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
calfn, cal_filelen);
ret = -EINVAL;
goto error;
}
/* Calibration package? */
if (memcmp(new_buf, blob_magic_string, sizeof(blob_magic_string)) == 0) {
/* MSF packaged file? They start with magic string 'BLOB' */
printf("Downloading calibration package format file %s\n", calfn);
ret = dload_blob(wl, "calload", new_buf, cal_filelen);
} else {
fprintf(stderr, "input file is missing calibration package magic string\n");
ret = -1;
goto error;
}
error:
if (new_buf)
free(new_buf);
if (fp)
fclose(fp);
return ret;
}
static int
wl_calload(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
char* fname;
BCM_REFERENCE(cmd);
/* argv is pointing at the calload command name to start */
if ((fname = *++argv) == NULL) {
/* too few arguments (none) */
return -EINVAL;
}
ret = process_cal_data(wl, fname);
return ret;
}
int
process_cal_dump(void *wl, char *fname_txcal, char *fname_rxcal)
{
int ret = 0;
unsigned long status = 0;
FILE *fblobp = NULL;
void *ptr = NULL;
uint32 dump_sz_total;
uint16 dump_sz_txcal, dump_sz_rxcal;
/* Read back TX calibration information */
if ((ret = wlu_var_getbuf_med(wl, "caldump", NULL, 0, &ptr)) < 0)
goto error;
dump_sz_total = dtoh32(*(int *)ptr);
printf("caldump: total dump size %d bytes\n", dump_sz_total);
if (dump_sz_total > (WLC_IOCTL_MEDLEN - 4)) {
fprintf(stderr, "caldump: total dump size too large\n");
ret = -EINVAL;
goto error;
}
ptr = (void *)((uint8 *)ptr + sizeof(dump_sz_total));
/* The TxCal data starts first. Its size is indicated in first two bytes
* (excluding size field itself - an uint16 field)
*/
dump_sz_txcal = dtoh16(*(uint16 *)ptr) + sizeof(dump_sz_txcal);
printf("\tTxCal dump size %d bytes\n", dump_sz_txcal);
if (dump_sz_total < (uint32)dump_sz_txcal) {
fprintf(stderr, "caldump: total dump size too small for TxCal\n");
ret = -EINVAL;
goto error;
}
/* Open the txcal bin file */
if (!(fblobp = fopen(fname_txcal, "wb"))) {
fprintf(stderr, "unable to open TxCal output file %s\n", fname_txcal);
perror(fname_txcal);
ret = -EINVAL;
goto error;
}
status = (unsigned long)fwrite((uint8 *)ptr, 1, dump_sz_txcal, fblobp);
if (status != dump_sz_txcal) {
fprintf(stderr, "unable to complete TxCal output (write %lu out of %u bytes)\n",
status, dump_sz_txcal);
ret = -EINVAL;
goto error;
}
ptr = (void *)((uint8 *)ptr + dump_sz_txcal);
fclose(fblobp);
fblobp = NULL;
/* The RxCal data starts after TxCal. Its size is indicated in first two bytes
* (but here, its size is derived from dump_sz_total and dump_sz_txcal))
*/
/* For backward comatible where only TxCal file is given, we just dump TxCal
* and return. If the RXCal file name is "NULL", we just return without any error
*/
if (fname_rxcal == NULL) {
fprintf(stderr, "can't find RxCal output file\n");
return ret;
}
dump_sz_rxcal = (uint16)(dump_sz_total - (uint32)dump_sz_txcal);
printf("\tRxCal dump size %d bytes\n", dump_sz_rxcal);
if (dump_sz_rxcal == 0) {
fprintf(stderr, "no RxCal data available\n");
ret = -EINVAL;
goto error;
}
/* Open the rxcal bin file */
if (!(fblobp = fopen(fname_rxcal, "wb"))) {
fprintf(stderr, "unable to open RxCal output file %s\n", fname_rxcal);
perror(fname_rxcal);
ret = -EINVAL;
goto error;
}
status = (unsigned long)fwrite((uint8 *)ptr, 1, dump_sz_rxcal, fblobp);
if (status != dump_sz_rxcal) {
fprintf(stderr, "unable to complete RxCal output (write %lu out of %u bytes)\n",
status, dump_sz_rxcal);
ret = -EINVAL;
goto error;
}
error:
if (fblobp)
fclose(fblobp);
return ret;
}
static int
wl_caldump(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
char *fname_txcal, *fname_rxcal;
BCM_REFERENCE(cmd);
/* argv is pointing at the command name to start */
if ((fname_txcal = *++argv) == NULL) {
/* too few arguments (none) */
return -EINVAL;
}
printf("caldump: dump TxCal to file %s\n", fname_txcal);
if ((fname_rxcal = *++argv) != NULL)
printf("caldump: dump RxCal to file %s\n", fname_rxcal);
ret = process_cal_dump(wl, fname_txcal, fname_rxcal);
return ret;
}
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;
}
/* 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 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);
}
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], "pll0") == 0) {
reg |= RADIO_2069_CORE_PLL0;
core_cmd = TRUE;
} else if (strcmp(argv[1], "pll1") == 0) {
reg |= RADIO_2069_CORE_PLL1;
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], "pll0") == 0) {
reg |= RADIO_2069_CORE_PLL0;
core_cmd = TRUE;
} else if (strcmp(argv[2], "pll1") == 0) {
reg |= RADIO_2069_CORE_PLL1;
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_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);
}
static int
wl_macregx(void *wl, cmd_t *cmd, char **argv)
{
int reg;
int size = 2;
int err;
char *p;
rw_reg_t rwt;
/* eat command name */
argv++;
/* required arg: reg offset */
if ((p = *argv) == NULL) {
err = BCME_USAGE_ERROR;
goto exit;
}
reg = strtol(p, NULL, 0);
argv++;
if (strcmp(cmd->name, "macregx") == 0) {
/* required arg: size of the register */
if ((p = *argv) == NULL) {
err = BCME_USAGE_ERROR;
goto exit;
}
size = strtol(p, NULL, 0);
argv++;
}
rwt.byteoff = htod32(reg);
rwt.size = htod32(size);
rwt.band = WLC_BAND_AUTO;
if ((p = *argv) == NULL) {
uint32 val;
/* GET cmd */
if ((err = wlu_iovar_getbuf(wl, cmd->name, &rwt,
sizeof(rwt), buf, WLC_IOCTL_SMLEN) < 0)) {
goto exit;
}
val = *((uint32 *)buf);
if (size == 4) {
printf("0x%08x\n", dtoh32(val));
} else {
printf("0x%04x\n", (uint16)dtoh32(val));
}
} else {
/* SET cmd */
/* required arg: set value */
if ((p = *argv) == NULL) {
err = BCME_USAGE_ERROR;
goto exit;
}
rwt.val = htod32(strtol(p, NULL, 0));
argv++;
if ((err = wlu_iovar_set(wl, cmd->name, &rwt, sizeof(rwt))) < 0) {
printf("Error setting variable %s\n", cmd->name);
return err;
}
}
exit:
return (err);
}
/*
* 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;
}
}
/* Commands that take a MAC address */
int
wl_macaddr(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 */
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)((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 = (float)(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 = (float)((float)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 += (float)(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)((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)((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)((float)ctr->txrate_succ * 0.5 / (float)ctr->acked) : 0.0;
/* Calculate Data rate */
data_rate = (ctr->time_delta) ?
(float)((float)ctr->throughput * 8.0 / (float)ctr->time_delta) :
0.0;
/* Calculate use percentage amongst throughput from all stations */
use = (total_throughput) ? (float)(data_rate / total_throughput * 100.0) :
0.0;
/* Calculate % airtime */
air = (ctr->time_delta) ? (float)((float)ctr->airtime * 100.0 /
(float) ctr->time_delta) : 0.0;
/* Calculate retry percentage */
rtr = (ctr->acked) ? (float)((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, full_params,
sizeof(*full_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, full_params,
sizeof(*full_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_phytbls(void *wl, char *dump_buf)
{
/* The IOVAR's used in iteration */
const char str_phytbl[] = "phytbl";
const char str_phytbl_page[] = "phydump_page";
const char str_phytbl_entry[] = "phydump_entry";
int phytbl_page = 0;
int phytbl_entry = 0;
int ret;
/* Initialize page and entry */
ret = wlu_iovar_setint(wl, str_phytbl_page, phytbl_page);
if (ret != BCME_OK) {
fprintf(stderr, "IOVAR %s is not supported by firmware\n", str_phytbl_page);
} else {
ret = wlu_iovar_setint(wl, str_phytbl_entry, phytbl_entry);
if (ret != BCME_OK) {
fprintf(stderr, "IOVAR %s is not supported by firmware\n",
str_phytbl_entry);
}
else {
/* Dump the whole table by iterating str_phytbl IOVAR */
do {
strncpy(dump_buf, str_phytbl, sizeof(str_phytbl));
if ((ret = wlu_iovar_getbuf(wl, "dump",
dump_buf, (int)strlen(dump_buf),
dump_buf, WL_DUMP_BUF_LEN)) >= 0) {
fputs(dump_buf, stdout);
memset(dump_buf, 0, WL_DUMP_BUF_LEN);
if (((ret = wlu_iovar_getint(wl, str_phytbl_page,
&phytbl_page)) >= 0) &&
(phytbl_page == 0)) {
ret = wlu_iovar_getint(wl, str_phytbl_entry,
&phytbl_entry);
}
}
/* Iteration ends when both page and entry wrap around */
} while ((ret >= BCME_OK) && ((phytbl_page != 0) || (phytbl_entry != 0)));
}
}
return ret;
}
static int
wlu_dump(void *wl, cmd_t *cmd, char **argv)
{
int ret, err, bcmerr;
char *dump_buf;
/* This suboption is only known to wl, IOVAR list won't show it */
const char str_phytbls[] = "phytbls";
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) {
err = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (!err && (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 if (!strncmp(*argv, str_phytbls, sizeof(str_phytbls))) {
ret = wlu_dump_phytbls(wl, dump_buf);
} 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, (int)strlen(dump_buf),
dump_buf, WL_DUMP_BUF_LEN);
}
if (ret >= BCME_OK) {
ret = BCME_OK;
fputs(dump_buf, stdout);
}
free(dump_buf);
return ret;
}
static int
wlu_dump_clr(void *wl, cmd_t *cmd, char **argv)
{
int ret;
/* skip the command name */
argv++;
/* call dump_clear for each category */
while (*argv != NULL) {
memset(buf, 0, WLC_IOCTL_MAXLEN);
ret = wlu_iovar_setbuf(wl, cmd->name, *argv, strlen(*argv),
buf, WLC_IOCTL_MAXLEN);
if (ret != BCME_OK) {
fprintf(stderr, "%s: error on %s\n", cmd->name, *argv);
return ret;
}
argv++;
}
return BCME_OK;
}
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;
/*
* Avoid wl utility to driver compatibility issues by reading a 'safe' amount of words from
* SPROM to determine the SPROM version that the driver supports, once the version is known
* the full SPROM contents can be read. At the moment sromrev12 is the largest.
*/
nw = MAX(MAX(SROM10_SIGN, SROM11_SIGN), SROM11_SIGN) + 1;
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] == SROM15_SIGNATURE) {
nw = SROM15_WORDS;
} else if (words[SROM11_SIGN] == SROM13_SIGNATURE) {
nw = SROM13_WORDS;
} else if (words[SROM11_SIGN] == SROM12_SIGNATURE) {
nw = SROM12_WORDS;
} else if (words[SROM11_SIGN] == SROM11_SIGNATURE) {
nw = SROM11_WORDS;
} else if (words[SROM10_SIGN] == SROM10_SIGNATURE) {
nw = SROM10_WORDS;
} else if (words[SROM16_SIGN] == SROM16_SIGNATURE) {
nw = SROM16_WORDS;
} else {
nw = SROM4_WORDS;
if ((words[SROM4_SIGN] != SROM4_SIGNATURE) &&
(words[SROM8_SIGN] != SROM4_SIGNATURE))
nw = SROM_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;
}
/* Since the SROM version known at this point, the entire SPROM contents can be read */
srt->nbytes = htod32(2 * nw);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
for (i = 0; i < nw; i++) {
if ((i % 8) == 0)
printf("\n srom[%03d]: ", i);
printf("0x%04x ", words[i]);
}
printf("\n");
return 0;
}
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);
#elif defined(DONGLEBUILD)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return 0;
#else
char *arg;
char *endptr;
FILE *fp = NULL;
int ret = 0, erase, srcrc;
uint i, len;
srom_rw_t *srt = (srom_rw_t *)buf;
char *tempbuf = NULL;
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 == SROM12_WORDS * 2) {
if ((srt->buf[SROM11_SIGN] != SROM12_SIGNATURE) &&
(srt->buf[SROM16_SIGN] != SROM16_SIGNATURE)) {
printf("\nFile %s is %d bytes but lacks a REV12/REV16 signature\n",
arg, SROM12_WORDS * 2);
ret = BCME_ERROR;
goto out;
}
} else if (len == SROM13_WORDS * 2) {
if (srt->buf[SROM11_SIGN] != SROM13_SIGNATURE) {
printf("\nFile %s is %d bytes but lacks a REV13/ signature\n",
arg, SROM13_WORDS * 2);
ret = BCME_ERROR;
goto out;
}
} else if (len == SROM15_WORDS * 2) {
if ((srt->buf[SROM11_SIGN] != SROM15_SIGNATURE) &&
(srt->buf[SROM16_SIGN] != SROM16_SIGNATURE)) {
printf("\nFile %s is %d bytes but lacks a REV15/REV16 signature\n",
arg, SROM15_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 {
tempbuf = malloc(SROM_MAX);
if (tempbuf == NULL) {
ret = BCME_NOMEM;
goto out;
}
if ((arg != NULL) && (len > MAX_IOCTL_TXCHUNK_SIZE) && (srt->byteoff != 0x55aa)) {
if (!(fp = fopen(arg, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", arg);
ret = BCME_BADARG;
goto out;
}
len = fread(tempbuf, 1, SROM_MAX + 1, fp);
memcpy(srt->buf, tempbuf, MAX_IOCTL_TXCHUNK_SIZE);
srt->byteoff = htod32(0);
srt->nbytes = htod32(MAX_IOCTL_TXCHUNK_SIZE);
ret = wlu_set(wl, cmd->set, buf, MAX_IOCTL_TXCHUNK_SIZE + 8);
memcpy(srt->buf, tempbuf + MAX_IOCTL_TXCHUNK_SIZE,
len - MAX_IOCTL_TXCHUNK_SIZE);
srt->byteoff = htod32(MAX_IOCTL_TXCHUNK_SIZE);
srt->nbytes = htod32(len - MAX_IOCTL_TXCHUNK_SIZE);
ret = wlu_set(wl, cmd->set, buf, len - MAX_IOCTL_TXCHUNK_SIZE + 8);
}
else {
ret = wlu_set(wl, cmd->set, buf, len + 8);
}
}
out:
fflush(stdout);
if (fp)
fclose(fp);
if (tempbuf)
free(tempbuf);
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);
#elif defined(DONGLEBUILD)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return 0;
#else
char *arg, *bufp;
FILE *fp = NULL;
int ret = 0;
uint32 len;
cis_rw_t cish;
char *cisp, *cisdata;
UNUSED_PARAMETER(cmd);
/* arg check -- error if no arg */
if (!*++argv)
return BCME_USAGE_ERROR;
memset((char*)&cish, 0, sizeof(cish));
/* Grab and move past optional output file argument */
if ((strcmp(*argv, "--pciecis") == 0) || (strcmp(*argv, "-p") == 0)) {
printf("Writing in PCIe CIS format\n");
cish.flags |= CISH_FLAG_PCIECIS; /* write CIS format bit */
if (!*++argv)
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);
/* 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;
}
/* Convert the endianess for non-zero fields */
cish.flags = htod16(cish.flags);
cish.nbytes = htod32(len); /* fill in length (offset is 0) */
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)
{
#if defined(DONGLEBUILD)
return 0;
#else
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 */
memset((char*)&cish, 0, sizeof(cish));
/* set up the buffer and do the get (+9 allows space for "ciswrite" string later) */
memset(buf + 9, 0, (WLC_IOCTL_MAXLEN - 9));
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 (dtoh16(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 */
memset((char*)&cish, 0, sizeof(cish));
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;
#endif
}
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 */
memset((char*)&cish, 0, sizeof(cish));
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 = dtoh16(cish.source);
cish.flags = dtoh16(cish.flags);
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 !defined(DONGLEBUILD)
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 /* !(CFE|DONGLEBUILD|IOPOS) -- has stdio filesystem */
#endif /* BWL_FILESYSTEM_SUPPORT */
done:
return ret;
}
#if defined(linux) || defined(DONGLEBUILD) || defined(__FreeBSD__) || \
defined(BWL_NO_INTERNAL_STDLIB_SUPPORT)
/* linux, MacOS, NetBSD: ffs is in the standard C library */
/* CFE, DONGLEBUILD & IOPOS: Not needed, the code below is ifdef out */
#else
static int
ffs(int i)
{
int j;
if (i != 0)
for (j = 0; j < 32; j++)
if (i & (1 << j))
return j + 1;
return 0;
}
#endif
#if !defined(DONGLEBUILD)
/* VX wants prototypes even for static functions. */
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.
*/
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.
*/
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_MACADDR2) {
if ((p = find_pattern(argv, "macaddr2", 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;
}
#endif
/** read/write caller supplied NVRAM variables in OTP or SROM */
static int
wlu_srvar(void *wl, cmd_t *cmd, char **argv)
{
#if defined(DONGLEBUILD)
return 0;
#else
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[SROM11_SIGN] == SROM12_SIGNATURE) {
sromrev = 12;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * SROM12_WORDS);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
} else if (words[SROM11_SIGN] == SROM13_SIGNATURE) {
sromrev = 13;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * SROM13_WORDS);
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
} else if (words[SROM11_SIGN] == SROM15_SIGNATURE) {
sromrev = 15;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * SROM15_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 > 32)) {
printf("Bad variable name: %s\n", name);
continue;
}
off = 0;
if (sromrev == 15)
srv = srvlookup(pci_srom15vars, name, nlen + 1, sromrev);
else
srv = srvlookup(pci_sromvars, name, nlen + 1, sromrev);
if (srv->name == NULL) {
int path;
if (sromrev == 15) {
printf("Variable %s does not exist in sromrev %d\n", name, sromrev);
return BCME_ERROR;
}
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 == 13) {
if (path == 0) {
off = SROM13_PATH0;
} else if (path == 1) {
off = SROM13_PATH1;
} else if (path == 2) {
off = SROM13_PATH2;
} else if (path == 3) {
off = SROM13_PATH3;
}
} else if (sromrev == 12) {
if (path == 0) {
off = SROM12_PATH0;
} else if (path == 1) {
off = SROM12_PATH1;
} else if (path == 2) {
off = SROM12_PATH2;
}
} else 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;
#endif
}
/* 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_MODE_SWITCH_VAL, "modesw"},
{WL_PORT_VAL, "port"},
{WL_DUAL_VAL, "dual"},
{WL_WSEC_VAL, "wsec"},
{WL_WSEC_DUMP_VAL, "wsec_dump"},
{WL_LOG_VAL, "log"},
{WL_BCNTRIM_VAL, "bcntrim"},
{WL_PFN_VAL, "pfn"},
{WL_REGULATORY_VAL, "regulatory"},
{WL_MPC_VAL, "mpc"},
{WL_APSTA_VAL, "apsta"},
{WL_DFS_VAL, "dfs"},
{WL_MUMIMO_VAL, "mumimo"},
{WL_MUMIMO_VAL, "mu"},
{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_SCAN_VAL, "scan"},
{WL_WOWL_VAL, "wowl"},
{WL_COEX_VAL, "coex"},
{WL_RTDC_VAL, "rtdc"},
{WL_PROTO_VAL, "proto"},
{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_TIMESTAMP_VAL, "time"},
{WL_PWRSEL_VAL, "lpc"},
{WL_TSO_VAL, "tso"},
{WL_MQ_VAL, "mq"},
{WL_TIMESTAMP_VAL, "chanlog"},
#ifdef WLP2PO
{WL_P2PO_VAL, "p2po"},
#endif
{WL_WNM_VAL, "wnm"},
{WL_TXBF_VAL, "txbf"},
{WL_PCIE_VAL, "pcie"},
{WL_MESH_VAL, "mesh"},
{WL_NATOE_VAL, "natoe"},
{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";
char c[32] = "0x";
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 {
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;
}
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 */
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" */
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 output of any of the wl_rate() iovars
*/
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 if (bw_val == WL_RSPEC_BW_10MHZ) {
bw = "10";
} else if (bw_val == WL_RSPEC_BW_5MHZ) {
bw = "5";
} else if (bw_val == WL_RSPEC_BW_2P5MHZ) {
bw = "2.5";
} 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-11, and next char is 's' or 'x' */
/* std MCS 0-9 and prop MCS 10-11 */
if (mcs < 0 || mcs > 11 || 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 && strcmp(mo.valstr, "2.5")) {
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 == 2) {
if (strcmp(mo.valstr, "2.5")) {
fprintf(stderr,
"%s: unexpected bandwidth specified \"%s\", "
"expected 2.5, 5, 10, 20, 40, 80, or 160\n",
fn_name, mo.valstr);
goto exit;
}
bw = WL_RSPEC_BW_2P5MHZ;
} else if (mo.val == 5) {
bw = WL_RSPEC_BW_5MHZ;
} else if (mo.val == 10) {
bw = WL_RSPEC_BW_10MHZ;
} else 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 2.5, 5, 10, 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;
} /* wl_rate */
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);
}
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_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 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_txbf_rateset(void *wl, cmd_t *cmd, char **argv)
{
wl_txbf_rateset_t rs;
int error = 0;
argv++;
if ((error = wlu_iovar_get(wl, cmd->name, &rs, sizeof(rs))) < 0)
return (error);
/* Get txbf rateset */
if (*argv == NULL) {
printf(" OFDM: ");
dump_rateset(rs.txbf_rate_ofdm, dtoh32(rs.txbf_rate_ofdm_cnt));
printf("\n");
wl_print_txbf_mcsset((char *)rs.txbf_rate_mcs, " ");
wl_print_txbf_vhtmcsset((uint16 *)rs.txbf_rate_vht, " ");
printf("BCM OFDM: ");
dump_rateset(rs.txbf_rate_ofdm_bcm, dtoh32(rs.txbf_rate_ofdm_cnt_bcm));
printf("\n");
wl_print_txbf_mcsset((char *)rs.txbf_rate_mcs_bcm, "BCM");
wl_print_txbf_vhtmcsset((uint16 *)rs.txbf_rate_vht_bcm, "BCM");
} else { /* Set txbf rateset */
error = wl_parse_txbf_rateset(&rs, argv);
if (!error) {
rs.txbf_rate_ofdm_cnt = htod32(rs.txbf_rate_ofdm_cnt);
rs.txbf_rate_ofdm_cnt_bcm = htod32(rs.txbf_rate_ofdm_cnt_bcm);
error = wlu_iovar_set(wl, cmd->name, &rs, sizeof(rs));
}
}
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 != VHT_CAP_MCS_0_7_RATEMAP) && /* vht mcs0-7 */
(mcs_mask != VHT_CAP_MCS_0_8_RATEMAP) && /* vht mcs0-8 */
(mcs_mask != VHT_CAP_MCS_0_9_RATEMAP) && /* vht mcs0-9 */
(mcs_mask != (VHT_CAP_MCS_0_9_RATEMAP |
VHT_PROP_MCS_10_11_RATEMAP))) { /* vht mcs0-11 */
fprintf(stderr, "Error: VHT rate mask must be 0 (disabled),"
" 0xff (MCS0-7), 0x1ff (MCS0-8), 0x3ff (MCS0-9) or "
"0xfff (MCS0-11).\n");
error = BCME_BADARG;
break;
}
rs->vht_mcs[mcs_index++] = mcs_mask;
}
}
return error;
}
static int
wl_parse_txbf_rateset(wl_txbf_rateset_t *rs, char **argv)
{
#define TXBF_RATE_OFDM 0
#define TXBF_RATE_MCS 1
#define TXBF_RATE_VHT 2
char* endp = NULL;
char* arg;
int i;
int error = 0;
int rate_type = TXBF_RATE_OFDM;
uint8 txbf_rate_mcs[TXBF_RATE_MCS_ALL];
uint8 txbf_rate_mcs_cnt = 0;
uint8 mcs_mask;
uint16 txbf_rate_vht[TXBF_RATE_VHT_ALL];
uint8 txbf_rate_vht_cnt = 0;
uint16 vht_mask;
uint8 txbf_rate_ofdm[TXBF_RATE_OFDM_ALL];
uint8 txbf_rate_ofdm_cnt;
int r;
bool specified_ofdm = FALSE;
bool specified_mcs = FALSE;
bool specified_vht = FALSE;
bool specified_bcm = FALSE;
/* First see if ofdm list */
arg = *argv;
if (stricmp(arg, "-m") != 0 && stricmp(arg, "-v") != 0) {
rate_type = TXBF_RATE_OFDM;
specified_ofdm = TRUE;
txbf_rate_ofdm_cnt = 0;
}
while ((arg = *argv++) != NULL) {
/* mcs rates */
if (!stricmp(arg, "-m")) {
rate_type = TXBF_RATE_MCS;
specified_mcs = TRUE;
txbf_rate_mcs_cnt = 0;
continue;
}
/* vht rates */
if (!stricmp(arg, "-v")) {
rate_type = TXBF_RATE_VHT;
specified_vht = TRUE;
txbf_rate_vht_cnt = 0;
continue;
}
/* Broadcom-to-Broadcom group */
if (!stricmp(arg, "-b")) {
specified_bcm = TRUE;
continue;
}
if (rate_type == TXBF_RATE_OFDM) {
/* Check if too many specified ofdm rates */
if (txbf_rate_ofdm_cnt >= TXBF_RATE_OFDM_ALL) {
fprintf(stderr, "ERR: more than max. of %d ofdm rates\n",
TXBF_RATE_OFDM_ALL);
return (BCME_USAGE_ERROR);
}
/* Convert user typed number to a 500kbps rate by multiplying by 2 */
r = (int)(strtoul(arg, &endp, 0) * 2);
if (endp == arg) {
fprintf(stderr, "ERR: failed to convert %s\n", arg);
return (BCME_USAGE_ERROR);
}
/* Check if valid ofdm rate */
for (i = 0; i < (int)sizeof(ofdm_rates); i++) {
/* match: it is a valid ofdm rate */
if (r == (ofdm_rates[i] & OFDM_RATE_MASK)) {
r = ofdm_rates[i]; /* will pick up basic rate bit too */
break;
}
}
if (i >= (int)sizeof(ofdm_rates)) {
fprintf(stderr, "ERR: %s is an invalid ofdm rate\n", arg);
return (BCME_USAGE_ERROR);
}
/* Strip trailing space */
while (isspace((int)endp[0]))
endp++;
/* No ofdm rate specified */
if ((rs->txbf_rate_ofdm_cnt == 0) && (r == 0)) {
fprintf(stderr, "ERR: no ofdm rate specified\n");
return (BCME_USAGE_ERROR);
}
txbf_rate_ofdm[txbf_rate_ofdm_cnt++] = r;
} else if (rate_type == TXBF_RATE_MCS) {
/* Check if exceeding max */
if (txbf_rate_mcs_cnt >= TXBF_RATE_MCS_ALL) {
fprintf(stderr, "ERR: exceed max. %d bitmask bytes; parsing %s\n",
TXBF_RATE_MCS_ALL, arg);
return (BCME_BADARG);
}
mcs_mask = (uint8)strtoul(arg, &endp, 16);
if (endp == arg) {
fprintf(stderr, "ERR: failed to convert %s\n", arg);
return (BCME_USAGE_ERROR);
}
txbf_rate_mcs[txbf_rate_mcs_cnt++] = mcs_mask;
} else if (rate_type == TXBF_RATE_VHT) {
/* Check if exceeding max */
if (txbf_rate_vht_cnt >= TXBF_RATE_VHT_ALL) {
fprintf(stderr, "ERR: exceed max. %d bitmasks; parsing %s\n",
TXBF_RATE_VHT_ALL, arg);
return (BCME_BADARG);
}
vht_mask = (uint16)strtoul(arg, &endp, 16);
if (endp == arg) {
fprintf(stderr, "ERR: failed to convert %s\n", arg);
return (BCME_USAGE_ERROR);
}
/* Validate bitmask value */
if (vht_mask > 0x0fff) {
fprintf(stderr, "ERR: vht bitmask must be 0 (disabled)"
" or up to a maximum of 0xfff (MCS0-11).\n");
return (BCME_BADARG);
}
txbf_rate_vht[txbf_rate_vht_cnt++] = vht_mask;
}
} /* while */
if (specified_mcs && !specified_bcm) {
for (i = 0; i < txbf_rate_mcs_cnt; i++)
rs->txbf_rate_mcs[i] = txbf_rate_mcs[i];
if (txbf_rate_mcs_cnt)
for (i = txbf_rate_mcs_cnt; i < TXBF_RATE_MCS_ALL; i++) /* clear trailer */
rs->txbf_rate_mcs[i] = 0;
}
if (specified_vht && !specified_bcm) {
for (i = 0; i < txbf_rate_vht_cnt; i++)
rs->txbf_rate_vht[i] = txbf_rate_vht[i];
if (txbf_rate_vht_cnt)
for (i = txbf_rate_vht_cnt; i < TXBF_RATE_VHT_ALL; i++) /* clear trailer */
rs->txbf_rate_vht[i] = 0;
}
if (specified_ofdm && !specified_bcm) {
for (i = 0; i < txbf_rate_ofdm_cnt; i++)
rs->txbf_rate_ofdm[i] = txbf_rate_ofdm[i];
rs->txbf_rate_ofdm_cnt = txbf_rate_ofdm_cnt;
}
if (specified_mcs && specified_bcm) {
for (i = 0; i < txbf_rate_mcs_cnt; i++)
rs->txbf_rate_mcs_bcm[i] = txbf_rate_mcs[i];
if (txbf_rate_mcs_cnt)
for (i = txbf_rate_mcs_cnt; i < TXBF_RATE_MCS_ALL; i++) /* clear trailer */
rs->txbf_rate_mcs_bcm[i] = 0;
}
if (specified_vht && specified_bcm) {
for (i = 0; i < txbf_rate_vht_cnt; i++)
rs->txbf_rate_vht_bcm[i] = txbf_rate_vht[i];
if (txbf_rate_vht_cnt)
for (i = txbf_rate_vht_cnt; i < TXBF_RATE_VHT_ALL; i++) /* clear trailer */
rs->txbf_rate_vht_bcm[i] = 0;
}
if (specified_ofdm && specified_bcm) {
for (i = 0; i < txbf_rate_ofdm_cnt; i++)
rs->txbf_rate_ofdm_bcm[i] = txbf_rate_ofdm[i];
rs->txbf_rate_ofdm_cnt_bcm = txbf_rate_ofdm_cnt;
}
return (error);
}
/*
* Get or Set LPC Params
* wl lpc_params \
* <rate_stab_thresh> <pwr_stab_thresh> <lpc_exp_time> <pwrup_slow_step>
* <pwrup_fast_step> <pwrdn_slow_step>
*/
static int
wl_power_sel_params(void *wl, cmd_t *cmd, char **argv)
{
int err, argc;
bool lpc_params_use_powersel_params;
lpc_params_t lpc_params;
UNUSED_PARAMETER(cmd);
lpc_params_use_powersel_params = (wlc_ver_major(wl) <= 5);
/* handle old powersel_params_t format support */
if (lpc_params_use_powersel_params) {
powersel_params_t pwrsel_params;
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;
}
/* handle new lpc_params_t format support */
argv++;
if (*argv == NULL) {
/* get current powersel params */
if ((err = wlu_iovar_get(wl, cmd->name, (void *) &lpc_params,
(sizeof(lpc_params_t)))) < 0)
return (err);
printf("- Link Power Control parameters (ver%d) -\n",
lpc_params.version);
printf("rate_stab_thresh\t= %d\npwr_stab_thresh\t\t= %d\n",
lpc_params.rate_stab_thresh, lpc_params.pwr_stab_thresh);
printf("lpc_exp_time\t\t= %d\npwrup_slow_step\t\t= %d\n",
lpc_params.lpc_exp_time, lpc_params.pwrup_slow_step);
printf("pwrup_fast_step\t\t= %d\npwrdn_slow_step\t\t= %d\n",
lpc_params.pwrup_fast_step, lpc_params.pwrdn_slow_step);
} else {
char *endptr;
/* Validate num of entries */
for (argc = 0; argv[argc]; argc++);
if (argc != 6)
return BCME_USAGE_ERROR;
lpc_params.version = WL_LPC_PARAMS_CURRENT_VERSION;
lpc_params.length = sizeof(lpc_params);
argc = 0;
lpc_params.rate_stab_thresh = strtol(argv[argc], &endptr, 0);
argc++;
lpc_params.pwr_stab_thresh = strtol(argv[argc], &endptr, 0);
argc++;
lpc_params.lpc_exp_time = strtol(argv[argc], &endptr, 0);
argc++;
lpc_params.pwrup_slow_step = strtol(argv[argc], &endptr, 0);
argc++;
lpc_params.pwrup_fast_step = strtol(argv[argc], &endptr, 0);
argc++;
lpc_params.pwrdn_slow_step = strtol(argv[argc], &endptr, 0);
/* Set powersel params */
err = wlu_iovar_set(wl, cmd->name, (void *) &lpc_params,
(sizeof(lpc_params_t)));
}
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) &&
(to.val != 160)) {
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 if (to.val == 80)
chanspec |= WL_CHANSPEC_BW_80;
else
chanspec |= WL_CHANSPEC_BW_160;
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_sc_chan(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
const char* fn_name = "wl_sc_chan";
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("SC Chanspec set to 0x%x\n", chanspec);
exit:
return err;
}
static int
wl_phy_vcore(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint32 val = 0;
uint8 bw80p80_cap = 0;
uint8 ncore = 0;
uint8 hw_swmask_txchain = 0;
uint8 hw_swmask_rxchain = 0;
bool tx_80p80_valid = FALSE;
bool rx_80p80_valid = FALSE;
/* 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;
bw80p80_cap = (val >> 12) & 0x1;
/*
* assume maximum core number is 4
* bit extension is needed for future > 4-core design
*/
ncore = (val >> 8) & 0xf;
hw_swmask_txchain = (val >> 4) & 0xf;
hw_swmask_rxchain = val & 0xf;
printf("REAL160:NO\n");
if (bw80p80_cap == 1) {
if (ncore == 4) {
/* 80p80 for 4-core, 4365 */
tx_80p80_valid |= hw_swmask_txchain == 0xf;
tx_80p80_valid |= hw_swmask_txchain == 0xa;
tx_80p80_valid |= hw_swmask_txchain == 0x5;
rx_80p80_valid |= hw_swmask_rxchain == 0xf;
rx_80p80_valid |= hw_swmask_rxchain == 0xa;
rx_80p80_valid |= hw_swmask_rxchain == 0x5;
if (!(tx_80p80_valid) || !(rx_80p80_valid)) {
printf("80p80:NO\n");
} else {
printf("80p80:YES\n");
if (hw_swmask_txchain == 0xf) {
printf("tx_vcore=%d txchain= %d\n", 1, 5);
printf("tx_vcore=%d txchain=%d\n", 2, 10);
printf("tx_vcore=%d txchain=%d\n", 3, 15);
} else if (hw_swmask_txchain == 0xa) {
printf("tx_vcore=%d txchain=%d\n", 2, 10);
} else {
printf("tx_vcore=%d txchain= %d\n", 1, 5);
}
if (hw_swmask_rxchain == 0xf) {
printf("rx_vcore=%d rxchain= %d\n", 1, 5);
printf("rx_vcore=%d rxchain=%d\n", 2, 10);
printf("rx_vcore=%d rxchain=%d\n", 3, 15);
} else if (hw_swmask_rxchain == 0xa) {
printf("rx_vcore=%d rxchain=%d\n", 2, 10);
} else {
printf("rx_vcore=%d rxchain= %d\n", 1, 5);
}
}
} else if (ncore == 2) {
/* 80p80 for 2-core, 4349 */
tx_80p80_valid |= hw_swmask_txchain == 0x3;
rx_80p80_valid |= hw_swmask_rxchain == 0x3;
if (!(tx_80p80_valid) || !(rx_80p80_valid)) {
printf("80p80:NO\n");
} else {
printf("80p80:YES\n");
printf("tx_vcore=%d txchain= %d\n", 1, 3);
printf("rx_vcore=%d rxchain= %d\n", 1, 3);
}
} else {
printf("!!!Number of cores should be 2 or 4!!!\n");
}
} else {
printf("80p80:NO\n");
}
return 0;
} else {
return BCME_UNSUPPORTED;
}
}
static int
wl_dfs_ap_move(void *wl, cmd_t *cmd, char **argv)
{
miniopt_t to;
void *ptr;
const char* fn_name = "wl_dfs_ap_move";
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;
int arg1;
struct wl_dfs_ap_move_status_v1 *status_v1;
struct wl_dfs_ap_move_status_v2 *status_v2;
char chanbuf[CHANSPEC_STR_LEN];
const char *dfs_state_str[DFS_SCAN_S_MAX] = {
"Radar Free On Channel",
"Radar Found On Channel",
"Radar Scan In Progress",
"Radar Scan Aborted",
"RSDB Mode switch in Progress For Scan"
};
/* toss the command name */
argv++;
/* GET */
if (!*argv) {
if (cmd->get < 0)
return -1;
if ((err = wlu_var_getbuf_sm(wl, cmd->name, NULL, 0, &ptr))) {
printf("err=%d \n", err);
return err;
}
/* Check for firmware version */
if (wlc_ver_major(wl) <= 5) {
status_v1 = (struct wl_dfs_ap_move_status_v1*)ptr;
if (status_v1->dfs_status != DFS_SCAN_S_IDLE) {
chanspec = wl_chspec32_from_driver(status_v1->chanspec);
if (chanspec != 0 && chanspec != INVCHANSPEC) {
wf_chspec_ntoa(chanspec, chanbuf);
printf("AP Target Chanspec %s (0x%x)\n", chanbuf, chanspec);
}
printf("%s\n", dfs_state_str[status_v1->dfs_status]);
err = wl_print_dfs_status(&status_v1->cac_status);
} else
printf("dfs AP move in IDLE state\n");
return err;
} else {
status_v2 = (struct wl_dfs_ap_move_status_v2*)ptr;
if (status_v2->version != WL_DFS_AP_MOVE_VERSION) {
err = BCME_UNSUPPORTED;
printf("err=%d version=%d\n", err, status_v2->version);
return err;
}
printf("version=%d, move status=%d\n", status_v2->version,
status_v2->move_status);
if (status_v2->move_status != (int8) DFS_SCAN_S_IDLE) {
chanspec = wl_chspec32_from_driver(status_v2->chanspec);
if (chanspec != 0 && chanspec != INVCHANSPEC) {
wf_chspec_ntoa(chanspec, chanbuf);
printf("AP Target Chanspec %s (0x%x)\n", chanbuf, chanspec);
}
printf("%s\n", dfs_state_str[status_v2->move_status]);
err = wl_print_dfs_status_all(&status_v2->scan_status);
} else {
printf("dfs AP move in IDLE state\n");
err = wl_print_dfs_status_all(&status_v2->scan_status);
}
return err;
}
}
/* SET */
arg1 = atoi(*argv);
chanspec = wf_chspec_aton(*argv);
if (arg1 == WL_DFS_AP_MOVE_ABORT || arg1 == WL_DFS_AP_MOVE_STUNT) {
err = wlu_iovar_setint(wl, cmd->name, arg1);
} else 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) {
fprintf(stderr,
"%s: invalid band %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
chanspec |= WL_CHANSPEC_BAND_5G;
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;
}
}
exit:
return err;
}
static int
wl_dfs_max_safe_tx(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint argc, num_vals = 0;
uint32 *pvals, vals[3];
char *endptr = NULL;
/* pop command name */
argv++;
/* arg count */
for (argc = 0; argc < 4 && argv[argc]; argc++); /* empty loop */
/* get case */
if (argc < 1) {
if (cmd->get < 0) {
return -1;
}
if ((err = wlu_var_getbuf_sm(wl, cmd->name, NULL, 0, (void**)&pvals)) || !pvals) {
printf("err=%d \n", err);
return err;
}
pvals[0] = dtoh32(pvals[0]);
pvals[1] = dtoh32(pvals[1]);
pvals[2] = dtoh32(pvals[2]);
printf("non-adjacent=%u%%, adjacent=%u%%, eu-weather=%u%%\n",
pvals[0], pvals[1], pvals[2]);
return err;
}
/* set case */
if (cmd->get < 0) {
return -1;
}
vals[num_vals++] = htod32(strtol(argv[0], &endptr, 0));
if (argc > 1) {
vals[num_vals++] = htod32(strtol(argv[1], &endptr, 0));
}
if (argc > 2) {
vals[num_vals++] = htod32(strtol(argv[2], &endptr, 0));
}
if ((err = wlu_var_setbuf_sm(wl, cmd->name, vals, sizeof(*vals)*(num_vals))) != BCME_OK) {
printf("err=%d\n", err);
return err;
}
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;
}
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));
}
}
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;
int err;
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) {
err = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (!err && (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, 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.
*/
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;
}
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 */
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, "chanspecs")) {
nchan = wl_parse_chanspec_list(valstr, params->channel_list,
WL_NUMCHANNELS);
if (nchan == -1) {
fprintf(stderr, "error parsing chanspec 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_t *params;
int err = 0;
params_size = WL_MAX_ROAMSCAN_DATSZ;
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);
if (!(argv[1])) {
#ifdef BCMDBG
printf("GET roam scan params\n");
#endif
/* no data to copy here for a get */
err = wlu_iovar_getbuf(wl, "roamscan_parms", params, 0,
buf, WLC_IOCTL_MEDLEN);
if (err) {
fprintf(stderr, "Error retrieving roamscan params: %d\n", err);
goto done;
}
#ifdef BCMDBG
prhex(NULL, (void *)buf, 64);
#endif
memset(params, 0, params_size);
memcpy(params, buf, params_size);
printf("Roam Scan Parameters:\n");
printf("scan_type: %d\n", dtoh32(params->scan_type));
printf("nprobes: %d\n", dtoh32(params->nprobes));
printf("active_time: %d\n", dtoh32(params->active_time));
printf("passive_time: %d\n", dtoh32(params->passive_time));
printf("home_time: %d\n", dtoh32(params->home_time));
/* print out the channels, if any */
if (params->channel_num) {
uint32 i;
uint32 chcount = dtoh32(params->channel_num);
printf("Channels:\n");
for (i = 0; i < chcount; i++)
printf("Channel number 0x%x\n", dtoh16(params->channel_list[i]));
}
/* No ssids to print out, ever */
goto done;
}
printf("Setting Roam Scan parameters \n");
err = wl_scan_prep(wl, cmd, argv, params, &params_size);
if (err)
goto done;
printf("params_size %d\n", params_size);
err = wlu_iovar_setbuf(wl, "roamscan_parms", params, params_size, buf, WLC_IOCTL_MEDLEN);
done:
free(params);
return err;
}
#ifdef WLRCC
static int
wl_roamchannels(void *wl, cmd_t *cmd, char **argv)
{
int params_size;
wl_roam_channel_list_t *params;
int i, chcount, chspec;
int err = 0;
UNUSED_PARAMETER(cmd);
params_size = sizeof(*params);
params = (wl_roam_channel_list_t *)malloc(params_size);
if (params == NULL) {
fprintf(stderr, "Error allocating %d bytes for scan channels\n", params_size);
return BCME_NOMEM;
}
memset(params, 0, params_size);
if (!(argv[1])) {
/* no data to copy here for a get */
err = wlu_iovar_getbuf(wl, "roamscan_channels", params, 0,
buf, WLC_IOCTL_MEDLEN);
if (err) {
fprintf(stderr, "Error retrieving roamscan channels: %d\n", err);
goto done;
}
memcpy(params, buf, params_size);
chcount = dtoh32(params->n);
printf("Roam Scan Channels: %d\n", chcount);
for (i = 0; i < chcount; i++) {
chspec = dtoh16(params->channels[i]);
wf_chspec_ntoa(chspec, buf);
printf("Channel: %s (0x%x)\n", buf, chspec);
}
goto done;
}
printf("Setting Roam Scan channels not supported!!!\n");
done:
free(params);
return err;
}
#endif /* WLRCC */
static int
wl_roam_prof(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint i;
void *ptr = NULL;
uint16 wl_rp_ver = WL_ROAM_PROF_VER_0;
uint wl_roam_prof_size = sizeof(wl_roam_prof_band_v1_t);
union {
wl_roam_prof_band_v1_t v1;
wl_roam_prof_band_v2_t v2;
} rp;
/* try to check wlc_ver first
* for older FW (WLC interface revision <= 5), wl_roam_prof ver 0 is used;
* for newer FW (WLC interface revision > 5), wl_roam_prof ver 1 is used;
*/
if (wlc_ver_major(wl) > 5) {
wl_rp_ver = WL_ROAM_PROF_VER_1;
wl_roam_prof_size = sizeof(wl_roam_prof_band_v2_t);
}
/* fill in some common fields among all roam_prof verisions */
++argv;
rp.v1.ver = wl_rp_ver;
if (*argv && (!strcmp(*argv, "b") || !strcmp(*argv, "2g"))) {
rp.v1.band = WLC_BAND_2G;
} else if (*argv && (!strcmp(*argv, "a") || !strcmp(*argv, "5g"))) {
rp.v1.band = WLC_BAND_5G;
} else
return -1; /* Missing band */
/* roam_prof version get */
rp.v1.len = 0;
if ((ret = wlu_var_getbuf(wl, cmd->name, &rp, 8, &ptr)) < 0)
return ret;
memcpy(&rp, ptr, sizeof(rp));
if (dtoh16(rp.v1.ver) > WL_ROAM_PROF_VER_1) {
printf("\tIncorrect version: expected up to %d; got %d\n",
WL_ROAM_PROF_VER_1, dtoh16(rp.v1.ver));
return BCME_VERSION;
}
if (!*++argv) {
/* roam_prof get is done above. */
if ((rp.v1.len % wl_roam_prof_size) != 0)
printf("bad length (=%d) in return data\n", rp.v1.len);
for (i = 0; i < WL_MAX_ROAM_PROF_BRACKETS; i++) {
if ((i * wl_roam_prof_size) > rp.v1.len) break;
if (rp.v1.ver > WL_ROAM_PROF_VER_0) {
/* The full scan period must be non-zero for valid roam profile */
if (rp.v2.roam_prof[i].fullscan_period == 0) break;
printf("flag:%02x RSSI[%d,%d] delta:%d(%s) boost:%d.by.%d "
"nfscan:%d period(full:%ds partial:%ds.x%d.%ds) "
"CU(trigger:%d%% duration:%ds)\n",
rp.v2.roam_prof[i].roam_flags,
rp.v2.roam_prof[i].roam_trigger,
rp.v2.roam_prof[i].rssi_lower,
rp.v2.roam_prof[i].roam_delta,
rp.v2.roam_prof[i].channel_usage ? "%" : "dBm",
rp.v2.roam_prof[i].rssi_boost_thresh,
rp.v2.roam_prof[i].rssi_boost_delta,
rp.v2.roam_prof[i].nfscan,
rp.v2.roam_prof[i].fullscan_period,
rp.v2.roam_prof[i].init_scan_period,
rp.v2.roam_prof[i].backoff_multiplier,
rp.v2.roam_prof[i].max_scan_period,
rp.v2.roam_prof[i].channel_usage,
rp.v2.roam_prof[i].cu_avg_calc_dur);
} else {
/* The full scan period must be non-zero for valid roam profile */
if (rp.v1.roam_prof[i].fullscan_period == 0) break;
printf("flag:%02x RSSI[%d,%d] delta:%d(dBm) boost:%d.by.%d "
"nfscan:%d period(full:%ds partial:%ds.x%d.%ds)\n",
rp.v1.roam_prof[i].roam_flags,
rp.v1.roam_prof[i].roam_trigger,
rp.v1.roam_prof[i].rssi_lower,
rp.v1.roam_prof[i].roam_delta,
rp.v1.roam_prof[i].rssi_boost_thresh,
rp.v1.roam_prof[i].rssi_boost_delta,
rp.v1.roam_prof[i].nfscan,
rp.v1.roam_prof[i].fullscan_period,
rp.v1.roam_prof[i].init_scan_period,
rp.v1.roam_prof[i].backoff_multiplier,
rp.v1.roam_prof[i].max_scan_period);
}
}
} else {
/* set */
memset(&rp.v1.roam_prof[0], 0, wl_roam_prof_size * WL_MAX_ROAM_PROF_BRACKETS);
for (i = 0; i < WL_MAX_ROAM_PROF_BRACKETS; i++) {
if (rp.v1.ver > WL_ROAM_PROF_VER_0) {
if (!*argv) break;
rp.v2.roam_prof[i].roam_flags = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].roam_trigger = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].rssi_lower = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].roam_delta = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].rssi_boost_thresh = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].rssi_boost_delta = atoi(*argv++);
if (!*argv) return -1;
rp.v2.roam_prof[i].nfscan = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].fullscan_period = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].init_scan_period = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].backoff_multiplier = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].max_scan_period = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].channel_usage = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v2.roam_prof[i].cu_avg_calc_dur = htod16(atoi(*argv++));
} else {
if (!*argv) break;
rp.v1.roam_prof[i].roam_flags = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].roam_trigger = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].rssi_lower = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].roam_delta = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].rssi_boost_thresh = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].rssi_boost_delta = atoi(*argv++);
if (!*argv) return -1;
rp.v1.roam_prof[i].nfscan = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v1.roam_prof[i].fullscan_period = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v1.roam_prof[i].init_scan_period = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v1.roam_prof[i].backoff_multiplier = htod16(atoi(*argv++));
if (!*argv) return -1;
rp.v1.roam_prof[i].max_scan_period = htod16(atoi(*argv++));
}
}
if (i == 0) {
return -1;
}
if (*argv) {
/* too many parameters */
return -1;
}
rp.v1.len = wl_roam_prof_size * i;
ret = wlu_var_setbuf(wl, cmd->name, &rp, 8 + rp.v1.len);
}
return ret;
}
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;
}
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);
#if defined(linux)
srand((unsigned)time(NULL));
params->sync_id = htod16(rand() & 0xffff);
#else
params->sync_id = htod16(4321);
#endif /* #if defined(linux) */
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;
}
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;
}
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;
}
static void bcm_print_vs_ie(uint8 *parse, int len)
{
bcm_tlv_t *ie;
while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) {
int len_tmp = 0;
printf("VS_IE:");
printf("%02x%02x", ie->id, ie->len);
while (len_tmp < ie->len) {
printf("%02x", ie->data[len_tmp]);
len_tmp++;
}
printf("\n");
if ((parse = (uint8 *)bcm_next_tlv(ie, &len)) == NULL)
break;
}
}
/* 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},
{ 144, 5720},
/* 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 (WPA2):\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;
}
}
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;
}
}
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("%s ", rsn ? "WPA2" : "WPA");
break;
case RSN_AKM_PSK:
printf("%s ", rsn ? "WPA2-PSK" : "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;
}
}
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;
}
}
}
/* vendor specific TLV match */
static bool bcm_vs_ie_match(uint8 *ie, uint8 *oui, int oui_len, uint8 type)
{
/* If the contents match the OUI and the type */
if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
!wlu_bcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
type == ie[TLV_BODY_OFF + oui_len]) {
return TRUE;
}
return FALSE;
}
static bcm_tlv_t *bcm_find_vs_ie(uint8 *parse, int len,
uint8 *oui, uint8 oui_len, uint8 oui_type)
{
bcm_tlv_t *ie;
while ((ie = bcm_parse_tlvs(parse, (int)len, DOT11_MNG_VS_ID))) {
if (bcm_vs_ie_match((uint8 *)ie, oui, oui_len, oui_type))
return ie;
if ((ie = bcm_next_tlv(ie, &len)) == NULL)
break;
}
return NULL;
}
static void
wl_dump_ext_cap(uint8* cp, uint len)
{
uint8 *parse = cp;
uint parse_len = len;
uint8 *ext_cap_ie;
if ((ext_cap_ie = wlu_parse_tlvs(parse, parse_len, DOT11_MNG_EXT_CAP_ID))) {
wl_ext_cap_ie_dump((bcm_tlv_t*)ext_cap_ie);
} else
printf("Extended Capabilities: Not_Available\n");
}
static void
wl_ext_cap_ie_dump(bcm_tlv_t* ext_cap_ie)
{
printf("Extended Capabilities: ");
if (ext_cap_ie->len >= CEIL(DOT11_EXT_CAP_IW, NBBY)) {
/* check IW bit */
if (isset(ext_cap_ie->data, DOT11_EXT_CAP_IW))
printf("IW ");
}
if (ext_cap_ie->len >= CEIL(DOT11_EXT_CAP_CIVIC_LOC, NBBY)) {
/* check Civic Location bit */
if (isset(ext_cap_ie->data, DOT11_EXT_CAP_CIVIC_LOC))
printf("Civic_Location ");
}
if (ext_cap_ie->len >= CEIL(DOT11_EXT_CAP_LCI, NBBY)) {
/* check Geospatial Location bit */
if (isset(ext_cap_ie->data, DOT11_EXT_CAP_LCI))
printf("Geospatial_Location ");
}
if (ext_cap_ie->len > 0) {
/* check 20/40 BSS Coexistence Management support bit */
if (isset(ext_cap_ie->data, DOT11_EXT_CAP_OBSS_COEX_MGMT))
printf("20/40_Bss_Coexist ");
}
if (ext_cap_ie->len >= CEIL(DOT11_EXT_CAP_BSSTRANS_MGMT, NBBY)) {
/* check BSS Transition Management support bit */
if (isset(ext_cap_ie->data, DOT11_EXT_CAP_BSSTRANS_MGMT))
printf("BSS_Transition");
}
printf("\n");
}
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, start_idx = 0;
bool start_idx_valid = FALSE;
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));
wl_dump_ext_cap((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");
if (CHSPEC_IS8080(bi->chanspec)) {
printf("\tChanspec: 5GHz channel %d-%d 80+80MHz (0x%x)\n",
wf_chspec_primary80_channel(bi->chanspec),
wf_chspec_secondary80_channel(bi->chanspec),
bi->chanspec);
}
else {
printf("\tChanspec: %sGHz channel %d %dMHz (0x%x)\n",
CHSPEC_IS2G(bi->chanspec)?"2.4":"5", CHSPEC_CHANNEL(bi->chanspec),
(CHSPEC_IS160(bi->chanspec) ?
160:(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 HT MCS :");
for (mcs_idx = 0; mcs_idx < (MCSSET_LEN * 8); mcs_idx++) {
if (isset(bi->basic_mcs, mcs_idx) && !start_idx_valid) {
printf(" %d", mcs_idx);
start_idx = mcs_idx;
start_idx_valid = TRUE;
}
if (!isset(bi->basic_mcs, mcs_idx) && start_idx_valid) {
if ((mcs_idx - start_idx) > 1)
printf("-%d", (mcs_idx - 1));
start_idx_valid = FALSE;
}
}
printf("\n");
if (bi->vht_cap) {
int i;
uint mcs, rx_mcs, prop_mcs = VHT_PROP_MCS_MAP_NONE;
char *mcs_str, *rx_mcs_str;
if (bi->vht_mcsmap) {
printf("\tNegotiated VHT MCS:\n");
for (i = 1; i <= VHT_CAP_MCS_MAP_NSS_MAX; i++) {
mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i, dtoh16(bi->vht_mcsmap));
/* roundup to be in sync with driver
* wlc_bss2wl_bss().
*/
if (dtoh16(bi->length) >= (OFFSETOF(wl_bss_info_t,
vht_mcsmap_prop) +
ROUNDUP(dtoh32(bi->ie_length), 4) +
sizeof(uint16))) {
prop_mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i,
dtoh16(bi->vht_mcsmap_prop));
}
mcs_str =
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-9 " :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8 " :
(mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7 " :
" -- ")));
if (prop_mcs != VHT_PROP_MCS_MAP_NONE)
mcs_str =
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-11 " :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8, 10-11" :
(mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7, 10-11" :
" -- ")));
if (mcs != VHT_CAP_MCS_MAP_NONE) {
printf("\t\tNSS%d : %s \n", i,
mcs_str);
}
}
} else {
printf("\tSupported VHT MCS:\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));
rx_mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i,
dtoh16(bi->vht_rxmcsmap));
/* roundup to be in sync with driver
* wlc_bss2wl_bss().
*/
if (dtoh16(bi->length) >= (OFFSETOF(wl_bss_info_t,
vht_txmcsmap_prop) +
ROUNDUP(dtoh32(bi->ie_length), 4) +
sizeof(uint16))) {
prop_mcs = VHT_MCS_MAP_GET_MCS_PER_SS(i,
dtoh16(bi->vht_txmcsmap_prop));
}
mcs_str =
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-9 " :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8 " :
(mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7 " : " -- ")));
if (prop_mcs != VHT_PROP_MCS_MAP_NONE)
mcs_str =
(mcs == VHT_CAP_MCS_MAP_0_9 ? "0-11 " :
(mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8, 10-11" :
(mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7, 10-11" :
" -- ")));
rx_mcs_str =
(rx_mcs == VHT_CAP_MCS_MAP_0_9 ? "0-9 " :
(rx_mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8 " :
(rx_mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7 " : " -- ")));
if (prop_mcs != VHT_PROP_MCS_MAP_NONE)
rx_mcs_str =
(rx_mcs == VHT_CAP_MCS_MAP_0_9 ? "0-11 " :
(rx_mcs == VHT_CAP_MCS_MAP_0_8 ? "0-8, 10-11" :
(rx_mcs == VHT_CAP_MCS_MAP_0_7 ? "0-7, 10-11" :
" -- ")));
if ((mcs != VHT_CAP_MCS_MAP_NONE) ||
(rx_mcs != VHT_CAP_MCS_MAP_NONE)) {
printf("\t\tNSS%d Tx: %s Rx: %s\n", i,
mcs_str, rx_mcs_str);
}
}
}
}
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");
}
if (bcm_find_vs_ie((uint8 *)(((uint8 *)bi) + dtoh16(bi->ie_offset)),
dtoh32(bi->ie_length),
(uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_OSEN) != NULL) {
printf("OSEN supported\n");
}
bcm_print_vs_ie((uint8 *)(((uint8 *)bi) + dtoh16(bi->ie_offset)),
dtoh32(bi->ie_length));
printf("\n");
}
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;
}
/* 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;
int num_chanspecs = 0;
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;
}
num_chanspecs += NUM_CHANSPECS_LIST_SIZE;
/* Add list */
list = (wl_uint32_list_t *)(buf + buflen);
list->count = htod32(num_chanspecs);
buflen += sizeof(uint32)*(num_chanspecs + 1);
/* if buflen is greater then WLC_IOCTL_MAXLEN return error */
ret = BCME_IOCTL_ERROR;
while (buflen <= WLC_IOCTL_MAXLEN) {
int bcmerr;
ret = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen);
if (ret == 0)
break;
/* If the error is not buffer too short break */
ret = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (ret < 0 || bcmerr != BCME_BUFTOOSHORT)
break;
/* If the error is buffer too short, increment the num_chanspecs */
num_chanspecs += NUM_CHANSPECS_LIST_SIZE;
list->count = htod32(num_chanspecs);
/* increase the buffer length by the space for the chanspecs added */
buflen += sizeof(uint32)*(NUM_CHANSPECS_LIST_SIZE);
}
/* Incase of error return */
if (ret) {
err = BCME_IOCTL_ERROR;
goto exit;
}
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, is80p80 = FALSE;
char abbrev[WLC_CNTRY_BUF_SZ] = ""; /* default.. current locale */
char chspec_str[CHANSPEC_STR_LEN];
char *country_abbrev;
int num_chanspecs = 0;
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') {
/* For 80+80 case, using flag is80p80 for it */
if (!strcmp(to.valstr, "80+80")) {
is80p80 = TRUE;
}
if ((!to.good_int) && (!is80p80)) {
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) &&
(to.val != 160) && (!is80p80)) {
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;
}
if (is80p80)
c |= WL_CHANSPEC_BW_8080;
else if (to.val == 160)
c |= WL_CHANSPEC_BW_160;
else
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|160|80+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 */
country_abbrev = buf + buflen;
strncpy(country_abbrev, abbrev, WLC_CNTRY_BUF_SZ);
buflen += WLC_CNTRY_BUF_SZ;
num_chanspecs += NUM_CHANSPECS_LIST_SIZE;
/* Add list */
list = (wl_uint32_list_t *)(buf + buflen);
list->count = htod32(num_chanspecs);
buflen += sizeof(uint32)*(num_chanspecs + 1);
/* if buflen is greater then WLC_IOCTL_MAXLEN return error */
ret = BCME_IOCTL_ERROR;
while (buflen <= WLC_IOCTL_MAXLEN) {
int bcmerr;
ret = wlu_get(wl, WLC_GET_VAR, &buf[0], buflen);
if (ret == 0)
break;
/* If the error is not buffer too short break */
ret = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (ret < 0 || bcmerr != BCME_BUFTOOSHORT)
break;
/* reconstruct the argument */
strcpy(buf, cmd->name);
/* Add chanspec argument */
*chanspec = c;
/* Add country abbrev */
strncpy(country_abbrev, abbrev, WLC_CNTRY_BUF_SZ);
/* If the error is buffer too short, increment the num_chanspecs */
num_chanspecs += NUM_CHANSPECS_LIST_SIZE;
list->count = htod32(num_chanspecs);
/* increase the buffer length by the space for the chanspecs added */
buflen += sizeof(uint32)*(NUM_CHANSPECS_LIST_SIZE);
}
/* Incase of error return */
if (ret) {
err = BCME_IOCTL_ERROR;
goto exit;
}
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"},
{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_1X_SHA256, "1X-SHA256"},
{WPA2_AUTH_FT, "FT"},
{WPA2_AUTH_PSK_SHA256, "PSK-SHA256"},
{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;
}
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;
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;
}
/* 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;
}
static int
wl_get_chanspec_txpwr_max(void *wl, cmd_t *cmd, char **argv)
{
int i, err;
char chspec_str[CHANSPEC_STR_LEN];
wl_chanspec_txpwr_max_t *chanspec_txpwr;
wl_chanspec_txpwr_max_t params;
miniopt_t to;
chanspec_t chanspec = 0;
int opt_err;
bool band_set = FALSE, bw_set = FALSE;
/* toss the command name */
argv++;
/* Validate arguments if any */
if (*argv) {
miniopt_init(&to, __FUNCTION__, 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 band\n",
__FUNCTION__, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val != 5) && (to.val != 2)) {
fprintf(stderr, "%s: invalid band %d\n",
__FUNCTION__, 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 bandwidth\n",
__FUNCTION__, to.valstr);
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 if (to.val == 80)
chanspec |= WL_CHANSPEC_BW_80;
else if (to.val == 160)
chanspec |= WL_CHANSPEC_BW_160;
else if (to.val == 8080)
chanspec |= WL_CHANSPEC_BW_8080;
else {
fprintf(stderr, "%s: invalid bandwidth %d\n",
__FUNCTION__, to.val);
err = BCME_BADARG;
goto exit;
}
bw_set = TRUE;
}
}
if (!bw_set || !band_set) {
if (!band_set)
fprintf(stderr, "%s: you need to set a band, '-b <5|2>'\n",
__FUNCTION__);
if (!bw_set)
fprintf(stderr,
"%s: you need to set a bandwidth, '-w <20|40|80>'\n",
__FUNCTION__);
err = BCME_USAGE_ERROR;
goto exit;
}
}
/* convert chanspec to legacy if needed */
if (chanspec != 0) {
chanspec = wl_chspec_to_driver(chanspec);
if (chanspec == INVCHANSPEC) {
err = BCME_USAGE_ERROR;
goto exit;
}
}
memset(&params, 0, WL_CHANSPEC_TXPWR_MAX_LEN);
params.ver = WL_CHANSPEC_TXPWR_MAX_VER;
params.len = WL_CHANSPEC_TXPWR_MAX_LEN;
params.count = 1;
params.txpwr[0].chanspec = chanspec;
if ((err = wlu_iovar_getbuf(wl, cmd->name, &params, sizeof(params),
buf, WLC_IOCTL_MAXLEN)) < 0) {
return err;
}
chanspec_txpwr = (wl_chanspec_txpwr_max_t *)buf;
if (chanspec_txpwr->ver != WL_CHANSPEC_TXPWR_MAX_VER) {
fprintf(stderr, "Error: version [%d] mismatch Driver version:%d\n",
WL_CHANSPEC_TXPWR_MAX_VER, chanspec_txpwr->ver);
return err;
}
for (i = 0; i < (int)(dtoh32(chanspec_txpwr->count)); i++) {
chanspec = wl_chspec32_from_driver(chanspec_txpwr->txpwr[i].chanspec);
wf_chspec_ntoa(chanspec, chspec_str);
printf("%s\t(0x%04x)\t%2d.%02d(dbm)\n",
chspec_str, chanspec,
DIV_QUO(chanspec_txpwr->txpwr[i].txpwr_max, 4),
DIV_REM(chanspec_txpwr->txpwr[i].txpwr_max, 4));
}
printf("\n");
exit:
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 "8"
#define RATE_STR_LEN 64
#define CURPOWER_VER_STR "cptlv-"
#define WL_CAP_CMD "cap"
static int
wl_get_curpower_tlv_ver(void *wl)
{
int error;
int ret = 0;
/* Get the CAP variable; search for curpower ver */
strncpy(buf, WL_CAP_CMD, WLC_IOCTL_MAXLEN);
if ((error = wlu_get(wl, WLC_GET_VAR, buf, WLC_IOCTL_MEDLEN)) >= 0) {
char seps[] = " ";
char *token;
buf[WLC_IOCTL_MEDLEN] = '\0';
token = strtok(buf, seps);
while (token != NULL) {
if (!memcmp(token, CURPOWER_VER_STR, strlen(CURPOWER_VER_STR))) {
char *ver = &token[strlen(CURPOWER_VER_STR)];
ret = atoi(ver);
}
token = strtok(NULL, seps);
}
}
return ret;
}
static int
wl_get_current_power(void *wl, cmd_t *cmd, char **argv)
{
int err;
int mimo;
int i;
char chanspec_str[CHANSPEC_STR_LEN];
bool verbose = FALSE;
bool brief = FALSE;
int16 power_target;
char rate_str[RATE_STR_LEN];
int clk;
int val;
chanspec_t chanspec;
uint8 *ppr_ser;
uint32 pprsize;
uint32 txchain_bitmap = 0;
size_t ppr_rpt_size;
tx_pwr_rpt_t *ppr_wl = NULL;
ppr_t* ppr_board = NULL;
ppr_t* ppr_target = NULL;
ppr_t* ppr_reg = NULL;
uint8 ppr_dim = 0;
int tlv_ver = wl_get_curpower_tlv_ver(wl);
/* firmware will crash if clk = 0 while using curpower */
if ((err = wlu_get(wl, WLC_GET_CLK, &clk, sizeof(int))) < 0)
goto exit;
if (!clk) {
fprintf(stderr, "Error: clock not active, do wl up (if not done already) "
"and force mpc 0 to active clock\n");
err = BCME_ERROR;
goto exit;
}
if ((err = wlu_iovar_getint(wl, "chanspec", (int *)&val)) < 0) {
goto exit;
}
chanspec = wl_chspec32_from_driver(val);
if ((err = wlu_iovar_get(wl, "hw_txchain", &txchain_bitmap, sizeof(txchain_bitmap))) < 0)
goto exit;
/* Special cases for 4365 3x3 config: hw_txchain can take a value between [8-15];
* there are still 4 PHY cores but one or more cores are not used
*/
if (txchain_bitmap > 8 && txchain_bitmap <= 15)
ppr_dim = 4;
else
ppr_dim = bcm_bitcount((uint8 *)&txchain_bitmap, sizeof(uint8));
if (tlv_ver) {
if ((ppr_board = ppr_create(NULL, ppr_chanspec_bw(chanspec))) == NULL) {
err = BCME_NOMEM;
goto exit;
}
if ((ppr_target = ppr_create(NULL, ppr_chanspec_bw(chanspec))) == NULL) {
err = BCME_NOMEM;
goto exit;
}
if ((ppr_reg = ppr_create(NULL, ppr_chanspec_bw(chanspec))) == NULL) {
err = BCME_NOMEM;
goto exit;
}
pprsize = ppr_get_tlv_size(ppr_target, ppr_chanspec_bw(chanspec), ppr_dim);
} else {
pprsize = ppr_ser_size_by_bw(ppr_get_max_bw());
}
ppr_rpt_size = sizeof(tx_pwr_rpt_t) + pprsize*WL_TXPPR_SER_BUF_NUM;
ppr_wl = (tx_pwr_rpt_t *)malloc(ppr_rpt_size);
if (!tlv_ver) {
for (i = 0; i < WL_TXPPR_SER_BUF_NUM; i++) {
ppr_ser = ppr_wl->pprdata + i*pprsize;
ppr_init_ser_mem_by_bw(ppr_ser, ppr_get_max_bw(), pprsize);
}
}
if (ppr_wl == NULL) {
fprintf(stderr, "Allocating mem failed for curpower\n");
err = BCME_NOMEM;
goto exit;
}
memset(ppr_wl, WL_RATE_DISABLED, ppr_rpt_size);
ppr_wl->ppr_len = pprsize;
ppr_wl->version = TX_POWER_T_VERSION;
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, ppr_rpt_size)) < 0) {
fprintf(stderr, "Error: Curpower failed. ");
fprintf(stderr, "Bring up interface and disable mpc if necessary (wl mpc 0)\n");
goto exit;
}
/* 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 {
int8 temp_val;
int divquo, divrem;
bool neg;
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);
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);
ppr_ser = ppr_wl->pprdata;
if (tlv_ver) {
(void)ppr_convert_from_tlv(ppr_board, ppr_wl->pprdata, ppr_wl->ppr_len);
ppr_ser += ppr_wl->ppr_len;
(void)ppr_convert_from_tlv(ppr_target, ppr_ser, ppr_wl->ppr_len);
ppr_ser += ppr_wl->ppr_len;
(void)ppr_convert_from_tlv(ppr_reg, ppr_ser, ppr_wl->ppr_len);
} else {
/* Try non TLV decode */
if ((err = ppr_deserialize_create(NULL, ppr_wl->pprdata, ppr_wl->ppr_len,
&ppr_board)) != BCME_OK) {
fprintf(stderr, "Error: read ppr board limit failed\n");
goto exit;
}
ppr_ser += ppr_wl->ppr_len;
if ((err = ppr_deserialize_create(NULL, ppr_ser, ppr_wl->ppr_len,
&ppr_target)) != BCME_OK) {
fprintf(stderr, "Error: read ppr target power failed\n");
goto exit;
}
ppr_ser += ppr_wl->ppr_len;
if ((err = ppr_deserialize_create(NULL, ppr_ser, ppr_wl->ppr_len, &ppr_reg))
!= BCME_OK) {
fprintf(stderr, "Error: read ppr regulatory limits failed\n");
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", "Channel Width:");
switch (ppr_wl->channel_bandwidth) {
case WL_BW_2P5MHZ:
printf("2.5MHz\n");
break;
case WL_BW_5MHZ:
printf("5MHz\n");
break;
case WL_BW_10MHZ:
printf("10MHz\n");
break;
case WL_BW_20MHZ:
printf("20MHz\n");
break;
case WL_BW_40MHZ:
printf("40MHz\n");
break;
case WL_BW_80MHZ:
printf("80MHz\n");
break;
case WL_BW_160MHZ:
printf("160MHz\n");
break;
case WL_BW_8080MHZ:
printf("80+80MHz\n");
break;
default:
fprintf(stderr, "Error: Unknown bandwidth %d\n",
ppr_wl->channel_bandwidth);
err = BCME_RANGE;
goto exit;
}
temp_val = (int8)(ppr_wl->user_target & 0xff);
divquo = DIV_QUO(temp_val, 4);
divrem = DIV_REM(temp_val, 4);
neg = (divrem < 0) || (divquo < 0);
divrem = ABS(divrem);
divquo = ABS(divquo);
divquo = neg ? -divquo : divquo;
printf("%-23s%d.%d dBm\n", "User Target:",
divquo, divrem);
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_array_row_print(ppr_reg, ppr_wl->channel_bandwidth,
get_reg_rate_index_from_ratespec(ppr_wl->last_tx_ratespec));
}
else
{
wl_txpwr_array_print(ppr_reg, ppr_wl->channel_bandwidth, verbose,
CHSPEC_IS5G(chanspec));
}
printf("\n");
printf("%-23s%d\n", "Core Index:", ppr_wl->display_core);
printf("Board Limits:\n");
if (brief)
{
wl_txpwr_array_row_print(ppr_board, 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++) {
power_target = (int8)ppr_wl->tx_power_max[i];
divquo = DIV_QUO(power_target, 4);
divrem = DIV_REM(power_target, 4);
neg = (divrem < 0) || (divquo < 0);
divrem = ABS(divrem);
divquo = ABS(divquo);
divquo = neg ? -divquo : divquo;
printf("%2d.%02d ", divquo, divrem);
}
printf("\n");
printf("Last est. power :\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO((int8)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 = (int8)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];
divquo = DIV_QUO(power_target, 4);
divrem = DIV_REM(power_target, 4);
neg = (divrem < 0) || (divquo < 0);
divrem = ABS(divrem);
divquo = ABS(divquo);
divquo = neg ? -divquo : divquo;
printf("%2d.%02d ", divquo, divrem);
}
else
{
printf("- ");
}
}
printf("\n");
printf("Last adjusted est. power :\t");
for (i = 0; i < ppr_wl->rf_cores; i++)
printf("%2d.%02d ",
DIV_QUO((int8)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((int8)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((int8)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);
}
if (ppr_reg != NULL) {
ppr_delete(NULL, ppr_reg);
}
}
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
*/
void
wl_txpwr_print_row(const char *label, uint8 chains, txpwr_row_t powers,
int8 unsupported_rate, int8 channel_bandwidth, bool verbose)
{
char tmp[] = "- ";
char rate2P5[] = "- ";
char rate5[] = "- ";
char rate10[] = "- ";
char rate20[] = "- ";
char rate20in40[] = "- ";
char rate40[] = "- ";
char rate80[] = "- ";
char rate20in80[] = "- ";
char rate40in80[] = "- ";
char rate160[] = "- ";
char rate20in160[] = "- ";
char rate40in160[] = "- ";
char rate80in160[] = "- ";
char rate8080[] = "- ";
char rate8080chan2[] = "- ";
char rate20in8080[] = "- ";
char rate40in8080[] = "- ";
char rate80in8080[] = "- ";
if (powers.pwr2p5 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr2p5/4);
strncpy(rate2P5, tmp, strlen(tmp));
}
if (powers.pwr5 != unsupported_rate) {
sprintf(tmp, "%2.2f ", (float)powers.pwr5/4);
strncpy(rate5, tmp, strlen(tmp));
}
if (powers.pwr10 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr10/4);
strncpy(rate10, tmp, strlen(tmp));
}
if (powers.pwr20 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20/4);
strncpy(rate20, tmp, strlen(tmp));
}
if (powers.pwr20in40 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20in40/4);
strncpy(rate20in40, tmp, strlen(tmp));
}
if (powers.pwr40 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr40/4);
strncpy(rate40, tmp, strlen(tmp));
}
if (powers.pwr80 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr80/4);
strncpy(rate80, tmp, strlen(tmp));
}
if (powers.pwr20in80 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20in80/4);
strncpy(rate20in80, tmp, strlen(tmp));
}
if (powers.pwr40in80 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr40in80/4);
strncpy(rate40in80, tmp, strlen(tmp));
}
if (powers.pwr20 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20/4);
strncpy(rate20, tmp, strlen(tmp));
}
if (powers.pwr160 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr160/4);
strncpy(rate160, tmp, strlen(tmp));
}
if (powers.pwr20in160 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20in160/4);
strncpy(rate20in160, tmp, strlen(tmp));
}
if (powers.pwr40in160 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr40in160/4);
strncpy(rate40in160, tmp, strlen(tmp));
}
if (powers.pwr80in160 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr80in160/4);
strncpy(rate80in160, tmp, strlen(tmp));
}
if (powers.pwr8080 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr8080/4);
strncpy(rate8080, tmp, strlen(tmp));
}
if (powers.pwr8080chan2 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr8080chan2/4);
strncpy(rate8080chan2, tmp, strlen(tmp));
}
if (powers.pwr20in8080 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr20in8080/4);
strncpy(rate20in8080, tmp, strlen(tmp));
}
if (powers.pwr40in8080 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr40in8080/4);
strncpy(rate40in8080, tmp, strlen(tmp));
}
if (powers.pwr80in8080 != unsupported_rate) {
sprintf(tmp, "%2.2f", (float)powers.pwr80in8080/4);
strncpy(rate80in8080, tmp, strlen(tmp));
}
printf("%-23s%d ", label, chains);
if (!verbose) {
switch (channel_bandwidth) {
case WL_BW_2P5MHZ:
printf("%s\n", rate2P5);
break;
case WL_BW_5MHZ:
printf("%s\n", rate5);
break;
case WL_BW_10MHZ:
printf("%s\n", rate10);
break;
case WL_BW_20MHZ:
printf("%s\n", rate20);
break;
case WL_BW_40MHZ:
printf("%s%s\n", rate20in40, rate40);
break;
case WL_BW_80MHZ:
printf("%s%s%s\n", rate20in80, rate40in80, rate80);
break;
case WL_BW_160MHZ:
printf("%s%s%s%s\n", rate20in160, rate40in160, rate80in160, rate160);
break;
case WL_BW_8080MHZ:
printf("%s%s%s%s%s\n", rate20in8080, rate40in8080, rate80in8080,
rate8080, rate8080chan2);
break;
}
} else {
printf("%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
rate2P5, rate5, rate10,
rate20, rate20in40, rate40, rate20in80, rate40in80, rate80,
rate20in160, rate40in160, rate80in160, rate160,
rate20in8080, rate40in8080, rate80in8080, rate8080, rate8080chan2);
}
}
static void
wl_txpwr_array_row_print(ppr_t *pprptr, int8 channel_bandwidth, reg_rate_index_t rate_index)
{
const char *label;
txpwr_row_t powers;
memset(&powers, (unsigned char)WL_RATE_DISABLED, sizeof(txpwr_row_t));
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_2P5MHZ:
powers.pwr2p5 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_2P5);
break;
case WL_BW_5MHZ:
powers.pwr5 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_5);
break;
case WL_BW_10MHZ:
powers.pwr10 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_10);
break;
case WL_BW_20MHZ:
powers.pwr20 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20);
break;
case WL_BW_40MHZ:
powers.pwr20in40 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN40);
powers.pwr40 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40);
break;
case WL_BW_80MHZ:
powers.pwr80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_80);
powers.pwr20in80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN80);
powers.pwr40in80 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40IN80);
break;
case WL_BW_160MHZ:
powers.pwr160 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_160);
powers.pwr20in160 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN160);
powers.pwr40in160 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40IN160);
powers.pwr80in160 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_80IN160);
break;
case WL_BW_8080MHZ:
powers.pwr8080 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_8080);
powers.pwr8080chan2 = wl_ppr_get_pwr(pprptr, rate_index,
WL_TX_BW_8080CHAN2);
powers.pwr20in8080 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_20IN8080);
powers.pwr40in8080 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_40IN8080);
powers.pwr80in8080 = wl_ppr_get_pwr(pprptr, rate_index, WL_TX_BW_80IN8080);
break;
}
wl_txpwr_print_row(label, ppr_group_table[group_id].chain, powers,
WL_RATE_DISABLED, channel_bandwidth, TRUE);
}
}
void
wl_txpwr_print_header(int8 channel_bandwidth, bool verbose)
{
if (!verbose)
{
switch (channel_bandwidth) {
case WL_BW_2P5MHZ:
printf("Rate Chains 2.5MHz\n");
break;
case WL_BW_5MHZ:
printf("Rate Chains 5MHz\n");
break;
case WL_BW_10MHZ:
printf("Rate Chains 10MHz\n");
break;
case WL_BW_20MHZ:
printf("Rate Chains 20MHz\n");
break;
case WL_BW_40MHZ:
printf("Rate Chains 20in40 40MHz\n");
break;
case WL_BW_80MHZ:
printf("Rate Chains 20in80 40in80 80MHz\n");
break;
case WL_BW_160MHZ:
printf(" 20in 40in 80in\n");
printf("Rate Chains 160 160 160 160\n");
break;
case WL_BW_8080MHZ:
printf(" 20in 40in 80in chan1 chan2\n");
printf("Rate Chains 80+80 80+80 80+80 80+80 80+80\n");
break;
}
} else {
printf(" 20in"
" 20in 40in 20in 40in 80in 20in 40in "
"80in chan1 chan2\n");
printf("Rate Chains 2.5 5 10 20 40 40"
" 80 80 80 160 160 160 160 80+80"
" 80+80 80+80 80+80 80+80\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 three parts: MCS + VHT8_9 and VHT10_11 */
if (ppr_group_table[i].rate_type == PPR_RATE_VHT)
i+=2; /* Skip VHT groups because it is alread printed */
}
}
/* Print power values for a group of rates. If not in verbose mode and rates
* are uniform, only one power value per channel is printed 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)
{
int8* rates[5] = {0}; /* Dynamic array of up to 5 ratesets for each channel in bw */
uint nchannels, rateset_sz;
uint vht_extra_rateset_sz = WL_RATESET_SZ_VHT_MCS - WL_RATESET_SZ_HT_MCS;
uint vht_prop_sz = WL_RATESET_SZ_VHT_MCS_P - WL_RATESET_SZ_VHT_MCS;
uint i, j;
const char *label;
uint buniform;
uint8 chains = ppr_group_table[gid].chain;
if (pprptr == NULL) {
fprintf(stderr, "illegal ppr data!\n");
return;
}
switch (bw) {
case WL_BW_2P5MHZ:
case WL_BW_5MHZ:
case WL_BW_10MHZ:
case WL_BW_20MHZ:
nchannels = 1;
break;
case WL_BW_40MHZ:
nchannels = 2;
break;
case WL_BW_80MHZ:
nchannels = 3;
break;
case WL_BW_160MHZ:
nchannels = 4;
break;
case WL_BW_8080MHZ:
nchannels = 5;
break;
default:
fprintf(stderr, "Error: Unknown bandwidth %d\n", bw);
return;
}
switch (type) {
case PPR_RATE_DSSS:
rateset_sz = sizeof(ppr_dsss_rateset_t);
break;
case PPR_RATE_OFDM:
rateset_sz = sizeof(ppr_ofdm_rateset_t);
break;
case PPR_RATE_HT:
rateset_sz = sizeof(ppr_ht_mcs_rateset_t);
break;
case PPR_RATE_VHT:
rateset_sz = sizeof(ppr_vht_mcs_rateset_t);
break;
default:
fprintf(stderr, "Error: Unknown rate %d\n", type);
return;
}
/* Allocate nchannel * rateset_sz array of powers */
for (i = 0; i < nchannels; i++) {
if ((rates[i] = (int8*)malloc(sizeof(int8) * rateset_sz)) == NULL) {
fprintf(stderr, "Error allocating rates array\n");
for (j = 0; j < i; j++) free(rates[j]);
return;
}
}
/* Load channel ratesets for specific type and group id into rate array */
switch (bw) {
case WL_BW_2P5MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_2P5, rates[0]);
break;
case WL_BW_5MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_5, rates[0]);
break;
case WL_BW_10MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_10, rates[0]);
break;
case WL_BW_20MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_20, rates[0]);
break;
case WL_BW_40MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_40, rates[0]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_20IN40, rates[1]);
break;
case WL_BW_80MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_80, rates[0]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_40IN80, rates[1]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_20IN80, rates[2]);
break;
case WL_BW_160MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_160, rates[0]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_80IN160, rates[1]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_40IN160, rates[2]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_20IN160, rates[3]);
break;
case WL_BW_8080MHZ:
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_8080, rates[0]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_80IN8080, rates[1]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_40IN8080, rates[2]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_20IN8080, rates[3]);
wl_txpwr_ppr_get_rateset(pprptr, type, gid, WL_TX_BW_8080CHAN2, rates[4]);
break;
}
if (type == PPR_RATE_DSSS && is5G) {
int tx_pwr_min = ppr_get_min(pprptr, WL_RATE_DISABLED);
for (i = 0; i < nchannels; i++) {
for (j = 0; j < rateset_sz; j++) {
if (rates[i][j] == tx_pwr_min)
rates[i][j] = WL_RATE_DISABLED;
}
}
}
/* Split VHT rates into different groups. */
if (type == PPR_RATE_VHT) {
rateset_sz -= (sizeof(ppr_vht_mcs_rateset_t) - sizeof(ppr_ht_mcs_rateset_t));
}
/* Uniform group if for each channel, all rates are equal */
buniform = !vb;
for (i = 0; i < nchannels && buniform; i++) {
buniform &= wl_array_check_val(rates[i], rateset_sz, rates[i][0]);
}
if (buniform) {
/* Uniform, so just print first rate */
label = get_clm_rate_group_label(gid);
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates, 0);
} else {
for (i = 0; i < rateset_sz; i++) {
label = ppr_table[*rate_index + i].label;
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates, i);
}
}
/* Print VHT8-9 and VHT10-11 as seperate groups */
if (type == PPR_RATE_VHT) {
/* VHT8-9 */
buniform = !vb;
for (i = 0; i < nchannels && buniform; i++) {
int8* vht_extra_rateset = &rates[i][rateset_sz];
buniform &= wl_array_check_val(vht_extra_rateset, vht_extra_rateset_sz,
vht_extra_rateset[0]);
}
if (buniform) {
/* Uniform, so just print first extra rate */
label = get_clm_rate_group_label(gid+1); /* VHT8-9 label */
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates, rateset_sz);
} else {
for (i = rateset_sz; i < (rateset_sz + vht_extra_rateset_sz); i++) {
label = ppr_table[*rate_index + i].label;
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates, i);
}
}
/* VHT10-11 */
buniform = !vb;
for (i = 0; i < nchannels && buniform; i++) {
int8* vht_prop_rateset = &rates[i][rateset_sz + vht_extra_rateset_sz];
buniform &= wl_array_check_val(vht_prop_rateset, vht_prop_sz,
vht_prop_rateset[0]);
}
if (buniform) {
/* Uniform, so just print first extra rate */
label = get_clm_rate_group_label(gid+2); /* VHT10-11 label */
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates,
rateset_sz + vht_extra_rateset_sz);
} else {
i = rateset_sz + vht_extra_rateset_sz;
for (; i < (rateset_sz + vht_extra_rateset_sz + vht_prop_sz);
i++) {
label = ppr_table[*rate_index + i].label;
if (strcmp(label, ""))
wl_txpwr_ppr_print_row(label, chains, bw, vb, rates, i);
}
}
}
*rate_index += rateset_sz;
if (type == PPR_RATE_VHT) {
*rate_index += (vht_extra_rateset_sz + vht_prop_sz);
}
for (i = 0; i < nchannels; i++) {
free(rates[i]);
}
}
/* Print row of power values for a specific rate. */
void wl_txpwr_ppr_print_row(const char* label, int8 chains, int8 bw, bool vb,
int8** rates, uint rate_index)
{
txpwr_row_t powers;
memset(&powers, (unsigned char)WL_RATE_DISABLED, sizeof(txpwr_row_t));
/* Set relevant power values based on bandwidth */
switch (bw) {
case WL_BW_2P5MHZ:
powers.pwr2p5 = rates[0][rate_index];
break;
case WL_BW_5MHZ:
powers.pwr5 = rates[0][rate_index];
break;
case WL_BW_10MHZ:
powers.pwr10 = rates[0][rate_index];
break;
case WL_BW_20MHZ:
powers.pwr20 = rates[0][rate_index];
break;
case WL_BW_40MHZ:
powers.pwr40 = rates[0][rate_index];
powers.pwr20in40 = rates[1][rate_index];
break;
case WL_BW_80MHZ:
powers.pwr80 = rates[0][rate_index];
powers.pwr40in80 = rates[1][rate_index];
powers.pwr20in80 = rates[2][rate_index];
break;
case WL_BW_160MHZ:
powers.pwr160 = rates[0][rate_index];
powers.pwr80in160 = rates[1][rate_index];
powers.pwr40in160 = rates[2][rate_index];
powers.pwr20in160 = rates[3][rate_index];
break;
case WL_BW_8080MHZ:
powers.pwr8080 = rates[0][rate_index];
powers.pwr80in8080 = rates[1][rate_index];
powers.pwr40in8080 = rates[2][rate_index];
powers.pwr20in8080 = rates[3][rate_index];
powers.pwr8080chan2 = rates[4][rate_index];
break;
default:
break;
}
wl_txpwr_print_row(label, chains, powers, WL_RATE_DISABLED, bw, vb);
}
/* Helper function which gets arbitrary rateset as a function of rate_type.
* Returns rateset into a int8 array.
*/
void wl_txpwr_ppr_get_rateset(ppr_t* pprptr, ppr_rate_type_t type,
clm_rate_group_id_t gid, wl_tx_bw_t bw, int8* rateset)
{
const ppr_group_t* group = &ppr_group_table[gid];
switch (type) {
case PPR_RATE_DSSS:
/* ASSERT(rateset_sz == sizeof(ppr_dsss_rateset_t)) */
ppr_get_dsss(pprptr, bw, group->chain,
(ppr_dsss_rateset_t*)rateset);
break;
case PPR_RATE_OFDM:
/* ASSERT(rateset_sz == sizeof(ppr_ofdm_rateset_t)) */
ppr_get_ofdm(pprptr, bw, group->mode, group->chain,
(ppr_ofdm_rateset_t*)rateset);
break;
case PPR_RATE_HT:
/* ASSERT(rateset_sz == sizeof(ppr_ht_mcs_rateset_t)) */
ppr_get_ht_mcs(pprptr, bw, group->nss, group->mode, group->chain,
(ppr_ht_mcs_rateset_t*)rateset);
break;
case PPR_RATE_VHT:
/* ASSERT(rateset_sz == sizeof(ppr_vht_mcs_rateset_t)) */
ppr_get_vht_mcs(pprptr, bw, group->nss, group->mode, group->chain,
(ppr_vht_mcs_rateset_t*)rateset);
break;
default:
/* ASSERT(0) */
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;
}
#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 infrastructure mode */
if (wlu_iovar_getint(wl, "infra_configuration", &infra) < 0) {
fprintf(stderr, "iovar infra_configuration GET failed, ret %d\n", ret);
goto exit;
}
infra = dtoh32(infra);
/* get authentication mode */
if ((ret = wlu_get(wl, WLC_GET_AUTH, &auth, sizeof(int))) < 0) {
fprintf(stderr, "ioctl WLC_GET_AUTH failed, ret %d\n", ret);
goto exit;
}
auth = dtoh32(auth);
/* get current wsec */
if (wlu_iovar_getint(wl, "wsec", &wsec) < 0) {
wsec = 0;
}
wsec = dtoh32(wsec);
/* get WPA_auth mode */
if ((ret = wlu_get(wl, WLC_GET_WPA_AUTH, &wpa_auth, sizeof(wpa_auth))) < 0) {
fprintf(stderr, "ioctl WLC_GET_WPA_AUTH failed, ret %d\n", ret);
goto exit;
}
wpa_auth = dtoh32(wpa_auth);
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 = WL_BSSTYPE_INDEP;
} else if (!stricmp(*argv, "bss") ||
!stricmp(*argv, "managed") ||
!strnicmp(*argv, "infra", 5)) {
infra = WL_BSSTYPE_INFRA;
} 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;
}
auth = WL_AUTH_OPEN_SYSTEM;
wpa_auth = WPA_AUTH_DISABLED;
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;
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 if (!stricmp(*argv, "wpa2-sha256"))
wpa_auth = WPA2_AUTH_1X_SHA256;
else if (!stricmp(*argv, "wpa2psk-sha256"))
wpa_auth = WPA2_AUTH_PSK_SHA256;
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) {
fprintf(stderr, "ioctl WLC_SET_INFRA failed, ret %d\n", ret);
goto exit;
}
/* set authentication mode */
auth = htod32(auth);
if ((ret = wlu_set(wl, WLC_SET_AUTH, &auth, sizeof(int))) < 0) {
fprintf(stderr, "ioctl WLC_SET_AUTH failed, ret %d\n", ret);
goto exit;
}
/* set wsec mode */
wsec = htod32(wsec);
if ((ret = wlu_iovar_setint(wl, "wsec", wsec)) < 0) {
fprintf(stderr, "iovar wsec SET failed, ret %d\n", ret);
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) {
fprintf(stderr, "ioctl WLC_SET_WPA_AUTH failed, ret %d\n", ret);
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;
}
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
*/
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;
}
/* 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(int8 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 int8
wl_mw_to_qdbm(uint16 mw)
{
uint8 qdbm;
int offset;
uint mw_uint = mw;
uint boundary;
/* handle boundary case */
if (mw_uint <= 1)
return WL_RATE_DISABLED;
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 += (int8)offset;
return (qdbm);
}
#define WLC_TXPWR_DB_FACTOR 4
#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;
int8 temp_val;
if (!*++argv) {
int divquo, divrem;
bool neg;
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;
temp_val = (int8)(val & 0xff);
divquo = DIV_QUO(temp_val, 4);
divrem = DIV_REM(temp_val, 4);
neg = (divrem < 0) || (divquo < 0);
divrem = ABS(divrem);
divquo = ABS(divquo);
divquo = neg ? -divquo : divquo;
printf("TxPower is %d qdbm, %d.%d dbm, %d mW Override is %s\n",
temp_val, divquo, divrem,
(temp_val < 4) ? 0 : wl_qdbm_to_mw(temp_val),
override ? "On" : "Off");
return 0;
} else {
/* for set */
bool unit_set = FALSE;
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;
unit_set = TRUE;
argv++;
}
else if (!strcmp(*argv, "-q")) {
unit = UNIT_QDBM;
unit_set = TRUE;
argv++;
}
else if (!strcmp(*argv, "-m")) {
unit = UNIT_MW;
unit_set = TRUE;
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 (!unit_set) {
if (val == -1) {
val = WLC_TXPWR_MAX; /* Max val of 127 qdbm */
unit = UNIT_QDBM;
} else if (val <= 0) {
return BCME_BADARG;
}
}
if ((val <= 0) && (unit == UNIT_MW)) {
return BCME_BADARG;
}
switch (unit) {
case UNIT_MW:
new_val = (uint8)wl_mw_to_qdbm((uint16)MIN(val, 0xffff));
break;
case UNIT_DBM:
if (val > (WLC_TXPWR_MAX / WLC_TXPWR_DB_FACTOR))
return BCME_BADARG;
val *= WLC_TXPWR_DB_FACTOR;
if (val < WL_RATE_DISABLED)
val = WL_RATE_DISABLED;
temp_val = (int8)val;
new_val = (uint8)temp_val; /* need to keep sign bit in low byte */
break;
case UNIT_QDBM:
if (val > WLC_TXPWR_MAX)
return BCME_BADARG;
if (val < WL_RATE_DISABLED)
val = WL_RATE_DISABLED;
temp_val = val;
new_val = (uint8)temp_val; /* need to keep sign bit in low byte */
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;
}
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_MEDLEN - sizeof(int)) / ETHER_ADDR_LEN;
uint len;
struct ether_addr tmp_ea;
bool found;
if (!*++argv) {
if (cmd->get < 0)
return -1;
maclist->count = htod32(max);
if ((ret = wlu_get(wl, cmd->get, maclist, WLC_IOCTL_MEDLEN)) < 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 (!strncmp(*argv, "none", strlen("none")) ||
!strncmp(*argv, "clear", strlen("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_MEDLEN)) < 0)
return ret;
/* Append to old list */
maclist->count = dtoh32(maclist->count);
if (!strncmp(*argv, "del", strlen("del"))) {
argv++;
ea = &tmp_ea;
while (*argv && maclist->count < max) {
if (!wl_ether_atoe(*argv, ea)) {
printf("Problem parsing MAC address \"%s\".\n", *argv);
return -1;
}
found = FALSE;
for (i = 0; i < maclist->count; i++) {
if (!memcmp(&maclist->ea[i], ea, ETHER_ADDR_LEN)) {
memcpy(&maclist->ea[i],
&maclist->ea[maclist->count-1], ETHER_ADDR_LEN);
maclist->count--;
found = TRUE;
}
}
if (!found)
printf("WARNING: cannot find any matched entry"
"for deleting %s\n", wl_ether_etoa(ea));
argv++;
}
} else {
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);
}
}
#define IOCTLECHO_MAX_SIZE 1000
#if IOCTLECHO_MAX_SIZE + 2 > WLC_IOCTL_MAXLEN
#error INVALID IOCTLECHO_MAX_SIZE
#endif
#ifndef ATE_BUILD
static int
wl_echo(void *wl, cmd_t *cmd, char **argv)
{
char *endptr;
uint len = 0;
uint i;
int ret;
UNUSED_PARAMETER(cmd);
if (argv[1])
len = strtoul(argv[1], &endptr, 0);
if (len > IOCTLECHO_MAX_SIZE) {
printf("maximum allowed size is : %d\n", IOCTLECHO_MAX_SIZE);
len = IOCTLECHO_MAX_SIZE;
}
memset(buf, 0, len+2);
for (i = 0; i < len; i++)
buf[2+i] = (char)i;
((ushort*)buf)[0] = len;
if ((ret = wlu_get(wl, WLC_ECHO, buf, len+2)) < 0)
return ret;
if (((ushort*)buf)[0] != len) {
printf("error read size is different then write size\n");
return -1;
}
for (i = 0; i < ((ushort*)buf)[0]; i++)
if (buf[2+i] != (char)i) {
printf("error read data is different then write data\n");
return -1;
}
printf("write buffer and read buffer are identical\n");
return 0;
}
#endif /* !ATE_BUILD */
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);
}
#ifdef linux
#define UPGRADE_BUFSIZE 512 /* upgrade buffer size */
#else
#define UPGRADE_BUFSIZE 1024 /* upgrade buffer size */
#endif /* linux */
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);
#elif defined(DONGLEBUILD)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return 0;
#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 */
}
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 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;
int interr = 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 */
interr = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (!interr && (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 */
interr = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if (!interr && (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;
}
}
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_dfs_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
void *ptr;
UNUSED_PARAMETER(argv);
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ret = wl_print_dfs_status(ptr);
return ret;
}
static int
wl_print_dfs_status(wl_dfs_status_t *dfs_status)
{
char chanspec_str[CHANSPEC_STR_LEN];
dfs_status->state = dtoh32(dfs_status->state);
dfs_status->duration = dtoh32(dfs_status->duration);
dfs_status->chanspec_cleared = wl_chspec_from_driver(dfs_status->chanspec_cleared);
if (dfs_status->state >= WL_DFS_CACSTATES) {
printf("Unknown dfs state %d.\n", dfs_status->state);
return -1;
}
printf("state %s time elapsed %dms radar channel cleared by dfs ",
dfs_cacstate_str[dfs_status->state], dfs_status->duration);
if (dfs_status->chanspec_cleared) {
printf("channel %s (0x%04X)\n",
wf_chspec_ntoa(dfs_status->chanspec_cleared, chanspec_str),
dfs_status->chanspec_cleared);
}
else {
printf("none\n");
}
return 0;
}
static int
wl_dfs_status_all(void *wl, cmd_t *cmd, char **argv)
{
int ret;
void *ptr;
UNUSED_PARAMETER(argv);
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
ret = wl_print_dfs_status_all(ptr);
return ret;
}
/* prints dfs status from one subset */
static int
wl_print_dfs_sub_status(wl_dfs_sub_status_t *sub)
{
char chanspec_str[CHANSPEC_STR_LEN];
sub->state = dtoh32(sub->state);
sub->duration = dtoh32(sub->duration);
sub->chanspec = wl_chspec_from_driver(sub->chanspec);
sub->chanspec_last_cleared = wl_chspec_from_driver(sub->chanspec_last_cleared);
sub->sub_type = dtoh16(sub->sub_type);
// state
if (sub->state >= WL_DFS_CACSTATES) {
printf("Unknown dfs state %d.\n", sub->state);
return -1;
}
printf("state: %s, time elapsed: %dms, chanspec: ",
dfs_cacstate_str[sub->state], sub->duration);
// chanspec
if (sub->chanspec) {
printf("%s (0x%04X), chanspec last cleared: ",
wf_chspec_ntoa(sub->chanspec, chanspec_str),
sub->chanspec);
} else {
printf("none, chanspec last cleared: ");
}
// chanspec last cleared
if (sub->chanspec_last_cleared) {
printf("%s (0x%04X), ",
wf_chspec_ntoa(sub->chanspec_last_cleared, chanspec_str),
sub->chanspec_last_cleared);
} else {
printf("none, ");
}
// sub type
printf("sub type: 0x%02x\n", sub->sub_type);
return 0;
}
/* prints dfs status of all subsets received */
static int
wl_print_dfs_status_all(wl_dfs_status_all_t *dfs_status_all)
{
int count;
int err;
if (dfs_status_all == NULL) {
return BCME_ERROR;
}
dfs_status_all->version = dtoh32(dfs_status_all->version);
dfs_status_all->num_sub_status = dtoh32(dfs_status_all->num_sub_status);
if (dfs_status_all->version != WL_DFS_STATUS_ALL_VERSION) {
err = BCME_UNSUPPORTED;
printf("err=%d version=%d\n", err, dfs_status_all->version);
return err;
}
printf("version: %d, num_sub_status: %d\n",
dfs_status_all->version, dfs_status_all->num_sub_status);
for (count = 0; count < dfs_status_all->num_sub_status; ++count) {
printf("@%d: ", count);
if ((err = wl_print_dfs_sub_status(&dfs_status_all->dfs_sub_status[count]))
!= BCME_OK) {
return err;
}
}
return BCME_OK;
}
static int
wl_radar_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint i;
wl_radar_status_t ra;
char chanspec_str[CHANSPEC_STR_LEN];
static const struct {
uint32 radar_type;
const char *radar_type_name;
} radar_names[] = {
{0, "NONE"},
{1, "ETSI_1"},
{2, "ETSI_2"},
{3, "ETSI_3"},
{4, "ETSI_4"},
{5, "S2"},
{6, "S3"},
{7, "UNCLASSIFIED"},
{8, "FCC-5"},
{9, "JP1-2/JP2-3"},
{10, "JP2-1"},
{11, "JP4"},
{12, "FCC_1"},
};
char radar_type_str[24];
UNUSED_PARAMETER(argv);
if ((ret = wlu_iovar_get(wl, cmd->name, &ra, sizeof(ra))) < 0)
return ret;
ra.ch = wl_chspec_from_driver(ra.ch);
if (ra.detected == FALSE) {
printf("NO RADAR DETECTED \n");
} else {
for (i = 0; i < ARRAYSIZE(radar_names); i++) {
if (radar_names[i].radar_type == ra.radartype)
snprintf(radar_type_str, sizeof(radar_type_str),
"%s", radar_names[i].radar_type_name);
}
if (ra.pretended == TRUE) {
printf("DFS: NONE ########## RADAR DETECTED ON CHAN %s \n",
wf_chspec_ntoa(ra.ch, chanspec_str));
} else {
if (ra.radartype == 8) {
printf("DFS: FCC-5 ########## RADAR DETECTED ON CHAN %s \n",
wf_chspec_ntoa(ra.ch, chanspec_str));
printf(" ########## lp_csect_single = %d, Time from last detection = %u, ",
ra.lp_csect_single, ra.timefromL);
printf(" = %dmin %dsec AT %dMS \n ",
ra.timefromL/60, ra.timefromL%60, ra.timenow);
} else {
printf("DFS: %s ########## RADAR DETECTED ON CHAN %s \n",
radar_type_str, wf_chspec_ntoa(ra.ch, chanspec_str));
printf(" ########## detected_pulse_index= %d, nconseq_pulses = %d, ",
ra.detected_pulse_index, ra.nconsecq_pulses);
printf(" Time from last detection = %u, = %dmin %dsec AT %dMS \n",
ra.timefromL, ra.timefromL/60, ra.timefromL%60, ra.timenow);
printf("Pruned Intv: ");
for (i = 0; i < 10; i++) {
printf("%d-%d ", ra.intv[i], i);
}
printf("\n");
printf("Pruned PW: ");
for (i = 0; i < 10; i++) {
printf("%i-%d ", ra.pw[i], i);
}
printf("\n");
printf("Pruned FM: ");
for (i = 0; i < 10; i++) {
printf("%i-%d ", ra.fm[i], i);
}
printf("\n");
}
}
}
return ret;
}
static int
wl_radar_sc_status(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint i;
wl_radar_status_t ra;
static const struct {
uint32 radar_type;
const char *radar_type_name;
} radar_names[] = {
{0, "NONE"},
{1, "ETSI_1"},
{2, "ETSI_2"},
{3, "ETSI_3"},
{4, "ETSI_4"},
{5, "S2"},
{6, "S3"},
{7, "UNCLASSIFIED"},
{8, "FCC-5"},
{9, "JP1-2/JP2-3"},
{10, "JP2-1"},
{11, "JP4"},
{12, "FCC_1"},
};
char radar_type_str[24];
/* Skip the command name */
argv++;
if (*argv == NULL) {
if ((ret = wlu_iovar_get(wl, cmd->name, &ra, sizeof(ra))) < 0)
return ret;
if (ra.detected == FALSE) {
printf("NO RADAR_SC DETECTED \n");
} else {
for (i = 0; i < ARRAYSIZE(radar_names); i++) {
if (radar_names[i].radar_type == ra.radartype)
snprintf(radar_type_str, sizeof(radar_type_str),
"%s", radar_names[i].radar_type_name);
}
if (ra.pretended == TRUE) {
printf("DFS: NONE ########## RADAR_SC DETECTED ON SC CHAN \n");
} else {
if (ra.radartype == 8) {
printf("DFS: FCC-5 ########## RADAR_SC DETECTED ON SC CHAN \n");
printf(" ########## lp_csect_single");
printf("= %d, Time from last detection = %u, ",
ra.lp_csect_single, ra.timefromL);
printf(" = %dmin %dsec AT %dMS \n ",
ra.timefromL/60, ra.timefromL%60, ra.timenow);
} else {
printf("DFS: %s ########## RADAR_SC DETECTED ON SC CHAN \n",
radar_type_str);
printf(" ########## detected_pulse_index= %d, nconseq_pulses = %d, ",
ra.detected_pulse_index, ra.nconsecq_pulses);
printf(" Time from last detection = %u, = %dmin %dsec AT %dMS \n",
ra.timefromL, ra.timefromL/60, ra.timefromL%60, ra.timenow);
printf("Pruned Intv: ");
for (i = 0; i < 10; i++) {
printf("%d-%d ", ra.intv[i], i);
}
printf("\n");
printf("Pruned PW: ");
for (i = 0; i < 10; i++) {
printf("%i-%d ", ra.pw[i], i);
}
printf("\n");
printf("Pruned FM: ");
for (i = 0; i < 10; i++) {
printf("%i-%d ", ra.fm[i], i);
}
printf("\n");
}
}
}
} else {
ret = atoi(*argv);
if (ret == 0) {
printf("Clear SC Radar Status \n");
ra.detected = FALSE;
return wlu_var_setbuf(wl, "clear_radar_sc_status",
&ra, sizeof(wl_radar_status_t));
}
}
return ret;
}
static int
wl_clear_radar_status(void *wl, cmd_t *cmd, char **argv)
{
wl_radar_status_t ra;
UNUSED_PARAMETER(argv);
printf("Clear Radar Status \n");
ra.detected = FALSE;
return wlu_var_setbuf(wl, cmd->name, &ra, sizeof(wl_radar_status_t));
}
/*
* 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.
*/
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;
}
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_pm_mute_tx(void *wl, cmd_t *cmd, char **argv)
{
wl_pm_mute_tx_t var;
var.version = htod16(WL_PM_MUTE_TX_VER);
var.len = htod16(sizeof(wl_pm_mute_tx_t));
if (!*++argv) {
goto missing_args;
}
var.enable = atoi(*argv);
if (var.enable) {
if (!*++argv) {
goto missing_args;
}
var.deadline = htod16(atoi(*argv));
}
return (wlu_var_setbuf(wl, cmd->name, &var, var.len));
missing_args:
return -1;
}
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;
}
int
wl_var_setint(void *wl, cmd_t *cmd, char **argv)
{
uint32 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 = strtoul(*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);
}
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_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);
}
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);
}
/* 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 of minimal size equal to the sizeof iovar name + param_len */
int
wlu_var_getbuf_minimal(void *wl, const char *iovar, void *param, int param_len, void **bufptr)
{
int len;
/* include the null */
len = strlen(iovar) + 1;
memset(buf, 0, (len + param_len));
strcpy(buf, iovar);
if (param_len)
memcpy(&buf[len], param, param_len);
*bufptr = buf;
return wlu_get(wl, WLC_GET_VAR, &buf[0], (len + param_len));
}
/* 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);
}
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);
}
#ifdef NOTYET
static int
wl_wlciovar_mkbuf(const char *iovar, int wlcidx, void *param,
int paramlen, void *bufptr, int buflen, int *perr)
{
const char *prefix = "wlc:";
return wl_prefixiovar_mkbuf(iovar, prefix, wlcidx, param, paramlen, bufptr, buflen, perr);
}
#endif
/*
* 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;
}
fputs((char *)p, stdout);
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);
}
int
wl_sta_info(void *wl, cmd_t *cmd, char **argv)
{
sta_info_v4_t *sta;
sta_info_v5_t *sta_v5;
struct ether_addr ea;
char *param;
int buflen, err;
int i;
char buf_chanspec[20];
uint32 rxdur_total = 0;
bool have_rxdurtotal = FALSE;
chanspec_t chanspec;
bool have_chanspec = FALSE;
wl_rateset_args_t *rateset_adv;
bool have_rateset_adv = FALSE;
strcpy(buf, *argv);
/* 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;
}
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_v4_t *)buf;
sta->ver = dtoh16(sta->ver);
sta->len = dtoh16(sta->len);
/* Report unrecognized version */
if (sta->ver < WL_STA_VER_4) {
printf(" ERROR: unsupported driver station info version %d\n", sta->ver);
return BCME_ERROR;
}
else if (sta->ver == WL_STA_VER_4) {
rxdur_total = dtoh32(sta->rx_dur_total);
have_rxdurtotal = TRUE;
if (sta->len >= STRUCT_SIZE_THROUGH(sta, rateset_adv)) {
chanspec = dtoh16(sta->chanspec);
wf_chspec_ntoa(chanspec, buf_chanspec);
have_chanspec = TRUE;
rateset_adv = &sta->rateset_adv;
have_rateset_adv = TRUE;
}
}
else if (sta->ver == WL_STA_VER_5) {
sta_v5 = (sta_info_v5_t *)buf;
chanspec = dtoh16(sta_v5->chanspec);
wf_chspec_ntoa(chanspec, buf_chanspec);
have_chanspec = TRUE;
rateset_adv = &sta_v5->rateset_adv;
have_rateset_adv = TRUE;
}
else {
printf(" ERROR: unknown driver station info version %d\n", sta->ver);
return BCME_ERROR;
}
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("[VER %d] STA %s:\n", sta->ver, *argv);
if (have_chanspec) {
printf("\t chanspec %s (0x%x)\n", buf_chanspec, chanspec);
}
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" : "");
}
if (sta->flags & WL_STA_SCBSTATS)
{
printf("\t tx total pkts: %d\n", dtoh32(sta->tx_tot_pkts));
printf("\t tx total 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));
if (have_rxdurtotal) {
printf("\t rx data dur: %u\n", rxdur_total);
}
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 - %d kbps\n",
dtoh32(sta->tx_rate), dtoh32(sta->tx_rate_fallback));
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));
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");
}
printf("\t tx total pkts sent: %d\n", dtoh32(sta->tx_pkts_total));
printf("\t tx pkts retries: %d\n", dtoh32(sta->tx_pkts_retries));
printf("\t tx pkts retry exhausted: %d\n", dtoh32(sta->tx_pkts_retry_exhausted));
printf("\t tx FW total pkts sent: %d\n", dtoh32(sta->tx_pkts_fw_total));
printf("\t tx FW pkts retries: %d\n", dtoh32(sta->tx_pkts_fw_retries));
printf("\t tx FW pkts retry exhausted: %d\n",
dtoh32(sta->tx_pkts_fw_retry_exhausted));
printf("\t rx total pkts retried: %d\n", dtoh32(sta->rx_pkts_retried));
}
/* Driver didn't return extended station info */
if (sta->len < sizeof(sta_info_v5_t)) {
return 0;
}
if (have_rateset_adv) {
wl_print_mcsset((char *)rateset_adv->mcs);
wl_print_vhtmcsset((uint16 *)rateset_adv->vht_mcs);
}
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 %d.%d\n", dtoh32(revinfo.corerev), dtoh32(revinfo.coreminorrev));
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));
if (revinfo.driverrev) {
uint32 rev = dtoh32(revinfo.driverrev);
printf("driverrev %d.%d.%d.%d\n",
(rev >> 24) & 0xFF,
(rev >> 16) & 0xFF,
(rev >> 8) & 0xFF,
rev & 0xFF);
} else {
printf("driverrev %d.%d.%d.%d\n",
dtoh32(revinfo.drvrev_major),
dtoh32(revinfo.drvrev_minor),
dtoh32(revinfo.drvrev_rc),
dtoh32(revinfo.drvrev_rc_inc));
}
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 %d.%d\n", dtoh32(revinfo.phyrev), dtoh32(revinfo.phyminorrev));
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 qosinfo;
int msp, max_sp_len, be, bk, vi, vo;
/* 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_AUTHREQ_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_VS_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, "vndr_ie", ie_setbuf, buflen);
else
err = wlu_bssiovar_setbuf(wl, "vndr_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_VS_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 = "vndr_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);
}
static int
wl_wlc_ver(void *wl, cmd_t *cmd, char **argv)
{
void *ptr;
int err;
wl_wlc_version_t *ver;
char buf[256];
UNUSED_PARAMETER(argv);
/* skip the command name */
argv++;
/* validate absence of arguments */
if (*argv) {
fprintf(stderr,
"\"%s\" wlc_ver iovar doesn't take any arguments\n", *argv);
return BCME_USAGE_ERROR;
}
if ((err = wlu_var_getbuf_sm(wl, cmd->name, NULL, 0, &ptr))) {
return err;
}
ver = (wl_wlc_version_t *)ptr;
sprintf(buf, "wlc_ver %d.%d\n" "epi_ver %d.%d.%d.%d\n",
ver->wlc_ver_major, ver->wlc_ver_minor, ver->epi_ver_major,
ver->epi_ver_minor, ver->epi_rc_num, ver->epi_incr_num);
fputs(buf, stdout);
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);
}
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;
}
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;
}
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);
}
#define WL_EVENTING_MASK_MAX_LEN 64
#define WL_EVENTINT_MAX_GET_SIZE (WL_EVENTING_MASK_MAX_LEN + EVENTMSGS_EXT_STRUCT_SIZE)
static int
wl_bitvecext(void *wl, cmd_t *cmd, char **argv)
{
int err, bcmerr;
eventmsgs_ext_t *eventmask_msg;
uint8 masksize;
err = 0;
bcmerr = 1;
/* set */
if (argv[1]) {
uint8 send_iovar_datasize;
/* send user mask size up to WL_EVENTING_MASK_MAX_LEN */
masksize = MIN((strlen(argv[1])/2), WL_EVENTING_MASK_MAX_LEN);
send_iovar_datasize = masksize + EVENTMSGS_EXT_STRUCT_SIZE;
eventmask_msg = (eventmsgs_ext_t*)malloc(send_iovar_datasize);
if (eventmask_msg == NULL) {
fprintf(stderr, "fail to allocate event_msgs"
"structure of %d bytes\n", send_iovar_datasize);
return BCME_NOMEM;
}
memset((void*)eventmask_msg, 0, send_iovar_datasize);
eventmask_msg->len = masksize;
eventmask_msg->command = EVENTMSGS_SET_MASK;
eventmask_msg->ver = EVENTMSGS_VER;
if (!(err = hexstrtobitvec(argv[1], eventmask_msg->mask, eventmask_msg->len))) {
err = wlu_var_setbuf(wl, cmd->name, (void*)eventmask_msg,
send_iovar_datasize);
if (err) {
int getint_error = 0;
getint_error = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if ((!getint_error) && (bcmerr == BCME_UNSUPPORTED)) {
uchar bitvec[WL_EVENTING_MASK_LEN];
printf("old firmware support only 128 events"
"setting only the first 128 events\n");
memset(bitvec, '\0', WL_EVENTING_MASK_LEN);
if (!(err = hexstrtobitvec(argv[1], bitvec,
WL_EVENTING_MASK_LEN)))
err = wlu_var_setbuf(wl, "event_msgs",
bitvec, WL_EVENTING_MASK_LEN);
}
}
}
else {
fprintf(stderr, "Invalid mask %d\n", eventmask_msg->len);
}
}
/* get */
else {
void *ptr;
char *mask;
int i;
bool skipzeros;
eventmsgs_ext_t *eventmask_msg_in;
skipzeros = TRUE;
/* input structure have no mask */
eventmask_msg = (eventmsgs_ext_t*)malloc(EVENTMSGS_EXT_STRUCT_SIZE);
if (eventmask_msg == NULL) {
return BCME_NOMEM;
}
memset((void*)eventmask_msg, 0, EVENTMSGS_EXT_STRUCT_SIZE);
/* command only used for set */
eventmask_msg->command = EVENTMSGS_NONE;
/* max read mask size is WL_EVENTING_MASK_MAX_LEN */
eventmask_msg->len = WL_EVENTING_MASK_MAX_LEN;
eventmask_msg->ver = EVENTMSGS_VER;
memset(buf, '\0', WL_EVENTINT_MAX_GET_SIZE);
if (!(err = wlu_var_getbuf_sm(wl, cmd->name, (void*)eventmask_msg,
EVENTMSGS_EXT_STRUCT_SIZE, &ptr))) {
eventmask_msg_in = (eventmsgs_ext_t*)ptr;
mask = (char *)eventmask_msg_in->mask;
masksize = eventmask_msg_in->len;
}
if (err) {
int getint_error = 0;
getint_error = wlu_iovar_getint(wl, "bcmerror", &bcmerr);
if ((!getint_error) && (bcmerr == BCME_UNSUPPORTED)) {
printf("old firmware support only 128 events"
"getting only the first 128 events\n");
if (!(err = wlu_var_getbuf_sm(wl, "event_msgs", NULL, 0, &ptr))) {
mask = (char *)ptr;
masksize = WL_EVENTING_MASK_LEN;
}
}
}
if (!err) {
printf("0x");
for (i = masksize - 1; i >= 0; i--) {
if (mask[i] || (i == 0))
skipzeros = FALSE;
if (skipzeros)
continue;
printf("%02x", mask[i] & 0xff);
}
printf("\n");
}
}
free(eventmask_msg);
return (err);
}
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;
}
/* 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;
}
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_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 linux
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 {
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);
if ((err = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN)))
return (err);
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);
close(fd);
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);
close(fd);
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);
close(fd);
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);
close(fd);
return (0);
}
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;
int err2 = BCME_OK;
struct sockaddr_ll sll;
struct ifreq ifr;
bcm_event_t *event;
uint32 status;
char *data;
int event_type;
uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */
bool revert_event_bit = FALSE;
wl_escan_result_t *escan_data;
struct escan_bss *escan_bss_head = NULL;
struct escan_bss *escan_bss_tail = NULL;
struct escan_bss *result;
fd_set rfds;
struct timeval tv;
int retval;
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));
strncpy(ifr.ifr_name, ((struct ifreq *)wl)->ifr_name, (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;
if (isclr(event_inds_mask, WLC_E_ESCAN_RESULT)) {
setbit(event_inds_mask, WLC_E_ESCAN_RESULT);
if ((err = wlu_iovar_set(wl, "event_msgs",
&event_inds_mask, WL_EVENTING_MASK_LEN)))
goto exit2;
revert_event_bit = TRUE;
}
fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM));
if (fd < 0) {
printf("Cannot create socket %d\n", fd);
err = -1;
goto exit2;
}
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
err = ioctl(fd, SIOCGIFINDEX, &ifr);
if (err < 0) {
printf("Cannot get index %d\n", err);
goto exit2fd;
}
/* 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 exit2fd;
}
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);
if (err != 0)
goto exit2fd;
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 exit2fd;
}
/* Set scan timeout */
tv.tv_sec = WL_EVENT_TIMEOUT;
tv.tv_usec = 0;
/* receive scan result */
while ((retval = select(fd+1, &rfds, NULL, NULL, &tv)) > 0) {
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))
continue;
/* We've already got this BSS. Update RSSI if necessary */
/* Prefer valid RSSI */
if (bi->RSSI == WLC_RSSI_INVALID)
break;
else if (bss->RSSI == WLC_RSSI_INVALID)
goto escan_update;
/* Prefer on-channel RSSI */
if (!(bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
(bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL))
break;
else if ((bi->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL) &&
!(bss->flags & WL_BSS_FLAGS_RSSI_ONCHANNEL))
goto escan_update;
/* Prefer probe response RSSI */
if ((bi->flags & WL_BSS_FLAGS_FROM_BEACON) &&
!(bss->flags & WL_BSS_FLAGS_FROM_BEACON))
break;
else if (!(bi->flags & WL_BSS_FLAGS_FROM_BEACON) &&
(bss->flags & WL_BSS_FLAGS_FROM_BEACON))
goto escan_update;
/* Prefer better RSSI */
if (bi->RSSI <= bss->RSSI)
break;
escan_update: /* Update known entry */
bss->RSSI = bi->RSSI;
bss->SNR = bi->SNR;
bss->phy_noise = bi->phy_noise;
bss->flags = bi->flags;
break;
}
if (!result) {
/* New BSS. Allocate memory and save it */
struct escan_bss *ebss = malloc(
OFFSETOF(struct escan_bss, bss) + 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 (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;
}
}
}
if (retval > 0) {
/* print scan results */
for (result = escan_bss_head; result; result = result->next) {
dump_bss_info(result->bss);
}
} else if (retval == 0) {
printf(" Scan timeout! \n");
} else {
printf(" Receive scan results failed!\n");
}
exit1:
/* free scan results */
result = escan_bss_head;
while (result) {
struct escan_bss *tmp = result->next;
free(result);
result = tmp;
}
free(data);
exit2fd:
close(fd);
exit2:
free(params);
/* Revert the event bit if appropriate */
if (revert_event_bit &&
!(err2 = wlu_iovar_get(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) {
clrbit(event_inds_mask, WLC_E_ESCAN_RESULT);
err2 = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN);
}
if (err2) {
fprintf(stderr, "Failed to revert event mask, error %d\n", err2);
}
return err ? err : err2;
}
/*
* Description:
* Primary job of this routine is to open a socket and listening
* on this socket for BRCM events and display it on console
*/
#define WL_EVENTS_BUFFER_SIZE 2048
static int
wl_event_check(void *wl, cmd_t *cmd, char **argv)
{
int fd, err, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
bcm_event_t * event;
uint32 status;
char* data;
uint event_type;
char time_str[TIME_STR_SZ];
struct tm ts;
struct timespec tspec;
uint policy;
pid_t pid;
struct sched_param sp;
uint max;
uint ret;
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));
} else if (wl) {
strncpy(ifnames, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1));
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, ifnames, (IFNAMSIZ - 1));
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);
close(fd);
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);
close(fd);
return -1;
}
data = (char*)malloc(WL_EVENTS_BUFFER_SIZE);
if (data == NULL) {
printf("Cannot not allocate %d bytes for events receive buffer\n",
ESCAN_EVENTS_BUFFER_SIZE);
close(fd);
return BCME_NOMEM;
}
/* make this process as rtprocess with highest priority */
/* get our scheduling policy */
pid = getpid();
policy = sched_getscheduler(pid);
switch (policy) {
case SCHED_OTHER:
{
max = sched_get_priority_max(SCHED_RR);
sp.sched_priority = max;
/* set the process scheduling also to RR */
sched_setscheduler(pid, SCHED_RR, &sp);
break;
}
case SCHED_RR:
{
max = sched_get_priority_max(SCHED_RR);
sp.sched_priority = max;
/* set the priority to max */
sched_setparam(pid, &sp);
break;
}
case SCHED_FIFO:
{
max = sched_get_priority_max(SCHED_RR);
sp.sched_priority = max;
sched_setscheduler(pid, SCHED_RR, &sp);
break;
}
case -1:
perror("sched_getscheduler fail!!");
break;
default:
printf("Unknown policy:%u!!Will not change policy to max!!\n", policy);
break;
}
while (1) {
octets = recv(fd, data, WL_EVENTS_BUFFER_SIZE, 0);
UNUSED_PARAMETER(octets);
event = (bcm_event_t *)data;
event_type = ntoh32(event->event.event_type);
/* get the RTC time in sec/usec */
ret = clock_gettime(CLOCK_REALTIME, &tspec);
if (ret) {
printf("clock_gettime error:%d\n", ret);
free(data);
close(fd);
return -1;
}
/* convert it in year-month-day-hour-min-sec */
ts = *localtime(&tspec.tv_sec);
strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", &ts);
/* print first line of events */
printf("%s:%09d %-8.16s %d:%s:", time_str, (uint)tspec.tv_nsec, ifnames,
event_type, wlu_lookup_name(wlc_event_names,
event_type));
/* event subtype interpretation */
switch (event_type)
{
case WLC_E_SET_SSID:
{
status = ntoh32(event->event.status);
if (status == WLC_E_STATUS_SUCCESS) {
uint32 ssid_len = ntoh32(event->event.datalen);
char ssid[SSID_FMT_BUF_LEN];
wl_format_ssid(ssid, (uint8*)(data+sizeof(bcm_event_t)),
ssid_len);
printf("SSID->\"%s\"", ssid);
}
else {
printf("SET_SSID_FAIL:%d", status);
}
break;
}
case WLC_E_JOIN:
{
status = ntoh32(event->event.status);
if (status == WLC_E_STATUS_SUCCESS) {
printf("JOIN_SUCCESS:");
printf("%02X:%02X:%02X:%02X:%02X:%02X",
event->event.addr.octet[0], event->event.addr.octet[1],
event->event.addr.octet[2], event->event.addr.octet[3],
event->event.addr.octet[4], event->event.addr.octet[5]);
}
else {
printf("JOIN_FAIL:%d", status);
}
break;
}
case WLC_E_AUTH:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("AUTH_REQ_SUCCESS:%d", reason);
}
else {
printf("AUTH_FAIL:%d", status);
}
break;
}
case WLC_E_AUTH_IND:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("AUTH_IND:%d", reason);
}
else {
printf("AUTH_IND_FAIL:%d", status);
}
break;
}
case WLC_E_DEAUTH:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("DEAUTH_REQ:%d", reason);
}
else {
printf("DEAUTH_REQ_FAIL:%d", status);
}
break;
}
case WLC_E_DEAUTH_IND:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("DEAUTH_IND:%d", reason);
}
else {
printf("DEAUTH_IND:%d", status);
}
break;
}
case WLC_E_ASSOC:
{
status = ntoh32(event->event.status);
if (status == WLC_E_STATUS_SUCCESS) {
printf("ASSOCRESP_SUCCESS");
}
else {
printf("ASSOCRESP_FAIL:%d", status);
}
break;
}
case WLC_E_DISASSOC:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("DISASSOC_REQ:%d", reason);
}
else {
printf("DISASSOC_REQ_FAIL:%d", status);
}
break;
}
case WLC_E_DISASSOC_IND:
{
status = ntoh32(event->event.status);
uint32 reason = ntoh32(event->event.reason);
if (status == WLC_E_STATUS_SUCCESS) {
printf("DISASSOC_IND:%d", reason);
}
else {
printf("DISASSOC_IND_FAIL:%d", status);
}
break;
}
case WLC_E_SCAN_COMPLETE:
{
status = ntoh32(event->event.status);
if (status == WLC_E_STATUS_SUCCESS) {
printf("SCAN_SUCCESS");
}
else {
printf("SCAN_FAIL:%d", status);
}
break;
}
case WLC_E_LINK:
{
uint16 flag = ntoh16(event->event.flags);
if (flag) {
printf("LINK_UP");
}
else {
printf("LINK_DOWN:%d", ntoh32(event->event.reason));
}
break;
}
default:
printf(" ");
break;
}
printf("\n");
fflush(stdout);
}
/* if we ever reach here */
free(data);
close(fd);
return (0);
}
#endif /* linux */
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_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.
*/
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;
}
#define MAX_HOST_ICMP_IPV6 4
int
wl_hostipv6_extended(void *wl, cmd_t *cmd, char **argv)
{
int ret;
uint16 *ip_addr;
wl_icmp_ipv6_cfg_t *cfg;
if (!*++argv) {
/* Get */
void *ptr = NULL;
uint32 i, k;
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return ret;
cfg = (wl_icmp_ipv6_cfg_t *)ptr;
if (cfg->version != WL_ICMP_IPV6_CFG_VERSION) {
printf("Version mismatch %d, expected %d\n", cfg->version,
WL_ICMP_IPV6_CFG_VERSION);
return BCME_VERSION;
}
/* Check length */
if (cfg->length != WL_ICMP_CFG_IPV6_LEN(cfg->num_ipv6)) {
printf("Length mismatch %d, calculated length %d\n", cfg->length,
(int)WL_ICMP_CFG_IPV6_LEN(cfg->num_ipv6));
return BCME_BADARG;
}
printf("Total %d Host ipv6 addresses\n", cfg->num_ipv6);
for (k = 0; k < cfg->num_ipv6; k++) {
ip_addr = (uint16*)cfg->host_ipv6[k].addr;
/* Print ipv6 Addr */
for (i = 0; i < 8; i++) {
printf("%x", ntoh16(ip_addr[i]));
if (i < 7)
printf(":");
}
printf("\r\n");
}
} else {
int argc, buf_size;
struct ipv6_addr ipa_set[MAX_HOST_ICMP_IPV6], null_ipa;
/* Set */
memset(null_ipa.addr, 0, IPV6_ADDR_LEN);
for (argc = 0; argc < MAX_HOST_ICMP_IPV6 && argv[argc]; argc++) {
if (!wl_atoipv6(argv[argc], &ipa_set[argc]))
return BCME_USAGE_ERROR;
if (!memcmp(null_ipa.addr, ipa_set->addr, IPV6_ADDR_LEN)) {
argc = 0;
break;
}
}
buf_size = WL_ICMP_CFG_IPV6_LEN(argc);
cfg = (wl_icmp_ipv6_cfg_t *)malloc(buf_size);
if (!cfg) {
printf("%s: Unable to allocate %d bytes!\n", __FUNCTION__, buf_size);
return BCME_NOMEM;
}
memset(cfg, 0, buf_size);
cfg->version = WL_ICMP_IPV6_CFG_VERSION;
cfg->fixed_length = WL_ICMP_CFG_IPV6_FIXED_LEN;
cfg->length = buf_size;
cfg->flags = 0;
if (!argc) {
cfg->flags |= WL_ICMP_IPV6_CLEAR_ALL;
}
cfg->num_ipv6 = argc;
memcpy(cfg->host_ipv6, ipa_set,
cfg->num_ipv6 * sizeof(struct ipv6_addr));
ret = wlu_var_setbuf(wl, cmd->name, cfg, buf_size);
free(cfg);
}
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.
*/
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;
}
/*
* 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.
*/
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;
}
static int
wl_mcast_ar(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
uint argc;
wl_rmc_entry_t *reply = NULL;
wl_rmc_entry_t rmc_entry;
void *ptr = NULL;
memset(&rmc_entry, 0, sizeof(wl_rmc_entry_t));
if (!*++argv) {
/* Get and display activer receiver address */
if ((ret = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0) {
return ret;
}
reply = (wl_rmc_entry_t*)ptr;
printf("%s\n", wl_ether_etoa(&reply->addr));
return 0;
}
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* Set activer receiver's mac address */
if (argc > 0 && argv[0]) {
printf(" %s \n", argv[0]);
if (!wl_ether_atoe(argv[0], &rmc_entry.addr)) {
if (strlen(argv[0]) == 1 && atoi(argv[0]) == 0) {
memset(&rmc_entry, 0, sizeof(wl_rmc_entry_t));
} else {
printf("Invalid argument, Please enter mac address\n"
"or enter \"0\" for auto ar selection\n");
return -1;
}
}
} else {
printf("Too few arguments\n");
return -1;
}
ret = wlu_var_setbuf(wl, cmd->name, &rmc_entry, sizeof(wl_rmc_entry_t));
return ret;
}
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_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;
}
#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;
}
#endif /* #ifdef SR_DEBUG */
static int
wl_antgain(void *wl, cmd_t *cmd, char **argv)
{
#if defined(DONGLEBUILD)
return 0;
#else
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;
#endif
}
/* Convert user's input in hex pattern to byte-size mask */
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;
}
#define MAX_PWR_STAT_TYPES 32
static int
wl_pwrstats(void *wl, cmd_t *cmd, char **argv)
{
wl_pwrstats_query_t *p_query;
wl_pwrstats_t *p_pwrstats;
void *ptr = NULL;
int rc = 0;
uint i, argc, len, taglen;
uint16 type;
/* Count <type> args and allocate buffer */
for (argv++, argc = 0; argv[argc]; argc++)
;
if (argc > MAX_PWR_STAT_TYPES) {
fprintf(stderr, "Currently limited to %d types in one query\n",
MAX_PWR_STAT_TYPES);
return -1;
}
len = OFFSETOF(wl_pwrstats_query_t, type) + argc * sizeof(uint16);
p_query = (wl_pwrstats_query_t *)malloc(len);
if (p_query == NULL) {
fprintf(stderr, "malloc failed to allocate %d bytes\n", len);
return -1;
}
/* Build a list of types */
p_query->length = argc;
for (i = 0; i < argc; i++, argv++) {
char *endptr;
p_query->type[i] = strtoul(*argv, &endptr, 0);
if (*endptr != '\0') {
fprintf(stderr, "Type '%s' (arg %d) not a number?\n", *argv, i);
free(p_query);
return -1;
}
}
/* Now issue the get with the query as parameter */
rc = wlu_var_getbuf(wl, cmd->name, p_query, len, &ptr);
free(p_query);
if (rc < 0)
return rc;
p_pwrstats = (wl_pwrstats_t *)ptr;
if (dtoh16(p_pwrstats->version) != WL_PWRSTATS_VERSION) {
printf("Power stats version mismatch\n");
return BCME_ERROR;
}
/* Basic information */
printf("Version: %d, Length %d bytes\n",
dtoh16(p_pwrstats->version), dtoh16(p_pwrstats->length));
/* Run down tags displaying content */
len = dtoh16(p_pwrstats->length) - WL_PWR_STATS_HDRLEN;
for (ptr = p_pwrstats->data; len >= 2 * sizeof(uint16); *(uint8**)&ptr += taglen) {
/* Grab tag/len words */
type = dtoh16(((uint16*)ptr)[0]);
taglen = dtoh16(((uint16*)ptr)[1]);
if ((taglen < 2 * sizeof(uint16)) || (taglen > len)) {
fprintf(stderr, "Bad len %d for tag %d, remaining len %d\n",
taglen, type, len);
rc = BCME_ERROR;
break;
}
if (taglen & 0xF000) {
fprintf(stderr, "Resrved bits in len %d for tag %d, remaining len %d\n",
taglen, type, len);
rc = BCME_ERROR;
break;
}
/* Tag-specific display */
switch (type) {
case WL_PWRSTATS_TYPE_PHY:
{
wl_pwr_phy_stats_t stats;
if (taglen < sizeof(wl_pwr_phy_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_phy_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
printf("PHY:\n"
" TX Duration: %u\n"
" RX Duration: %u\n",
dtoh32(stats.tx_dur),
dtoh32(stats.rx_dur));
}
break;
case WL_PWRSTATS_TYPE_SCAN:
{
wl_pwr_scan_stats_t stats;
if (taglen < sizeof(wl_pwr_scan_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_scan_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
printf("SCAN:\n"
" User-Scan:\tCount: %u\tDuration: %u\n"
" Assoc-Scan:\tCount: %u\tDuration: %u\n"
" Roam-Scan:\tCount: %u\tDuration: %u\n"
" PNO-Scan:\tCount: %u\tDuration: %u\n"
" Other-Scan:\tCount: %u\tDuration: %u\n",
dtoh32(stats.user_scans.count),
dtoh32(stats.user_scans.dur),
dtoh32(stats.assoc_scans.count),
dtoh32(stats.assoc_scans.dur),
dtoh32(stats.roam_scans.count),
dtoh32(stats.roam_scans.dur),
dtoh32(stats.pno_scans[0].count),
dtoh32(stats.pno_scans[0].dur),
dtoh32(stats.other_scans.count),
dtoh32(stats.other_scans.dur));
}
break;
case WL_PWRSTATS_TYPE_USB_HSIC:
{
wl_pwr_usb_hsic_stats_t stats;
if (taglen < sizeof(wl_pwr_usb_hsic_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_usb_hsic_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
printf("HSIC:\n"
" Suspend count: %u\n"
" Resume count: %u\n"
" Disconnect count: %u\n"
" Reconnect count: %u\n"
" Active duration: %u\n"
" Suspend duration: %u\n"
" Disconnect duration:%u\n",
dtoh32(stats.hsic.suspend_ct),
dtoh32(stats.hsic.resume_ct),
dtoh32(stats.hsic.disconnect_ct),
dtoh32(stats.hsic.reconnect_ct),
dtoh32(stats.hsic.active_dur),
dtoh32(stats.hsic.suspend_dur),
dtoh32(stats.hsic.disconnect_dur));
}
break;
case WL_PWRSTATS_TYPE_PCIE:
{
wl_pwr_pcie_stats_t stats;
if (taglen < sizeof(wl_pwr_pcie_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_pcie_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
if (dtoh32(stats.pcie.l0_cnt) == 0) {
printf("link stats are not supported for this pcie core\n");
}
printf("PCIE:\n"
" D3 Suspend count: %u\n"
" D0 Resume count: %u\n"
" PERST# assert count: %u\n"
" PERST# deassert count: %u\n"
" Active duration: %u ms\n"
" D3 Suspend duration: %u ms\n"
" PERST# duration:%u ms\n"
" l0 cnt:%u dur:%u usecs\n"
" l1 cnt:%u dur:%u usecs\n"
" l1_1 cnt:%u dur:%u usecs\n"
" l1_2 cnt:%u dur:%u usecs\n"
" l2 cnt:%u dur:%u usecs\n"
" LTR_ACTIVE Count %u Duration: %u ms\n"
" LTR_SLEEP Count %u Duration: %u ms\n"
" DeepSleep Count %u Duration: %u ms\n",
dtoh32(stats.pcie.d3_suspend_ct),
dtoh32(stats.pcie.d0_resume_ct),
dtoh32(stats.pcie.perst_assrt_ct),
dtoh32(stats.pcie.perst_deassrt_ct),
dtoh32(stats.pcie.active_dur),
dtoh32(stats.pcie.d3_suspend_dur),
dtoh32(stats.pcie.perst_dur),
dtoh32(stats.pcie.l0_cnt),
dtoh32(stats.pcie.l0_usecs),
dtoh32(stats.pcie.l1_cnt),
dtoh32(stats.pcie.l1_usecs),
dtoh32(stats.pcie.l1_1_cnt),
dtoh32(stats.pcie.l1_1_usecs),
dtoh32(stats.pcie.l1_2_cnt),
dtoh32(stats.pcie.l1_2_usecs),
dtoh32(stats.pcie.l2_cnt),
dtoh32(stats.pcie.l2_usecs),
dtoh32(stats.pcie.ltr_active_ct),
dtoh32(stats.pcie.ltr_active_dur),
dtoh32(stats.pcie.ltr_sleep_ct),
dtoh32(stats.pcie.ltr_sleep_dur),
dtoh32(stats.pcie.deepsleep_count),
dtoh32(stats.pcie.deepsleep_dur));
printf("\nIPC Stats\n");
printf(" Submissions:\tCount: %u\th2d drbl: %u\t",
dtoh32(stats.pcie.num_submissions),
dtoh32(stats.pcie.num_h2d_doorbell));
if (stats.pcie.num_h2d_doorbell)
printf("Avg per h2d drbl: %d.%d\n",
DIV_QUO(dtoh32(stats.pcie.num_submissions),
dtoh32(stats.pcie.num_h2d_doorbell)),
DIV_REM(dtoh32(stats.pcie.num_submissions),
dtoh32(stats.pcie.num_h2d_doorbell)));
else
printf(" Avg per h2d drbl: 0.0\n");
printf(" Completions:\tCount: %u\td2h drbl: %u\t",
dtoh32(stats.pcie.num_completions),
dtoh32(stats.pcie.num_d2h_doorbell));
if (stats.pcie.num_d2h_doorbell)
printf("Avg per d2h drbl: %d.%d\n",
DIV_QUO(dtoh32(stats.pcie.num_completions),
dtoh32(stats.pcie.num_d2h_doorbell)),
DIV_REM(dtoh32(stats.pcie.num_completions),
dtoh32(stats.pcie.num_d2h_doorbell)));
else
printf("Avg per d2h drbl: 0.0\n");
printf(" Rx Cmplt:\tCount: %u\trx_cmplt drbl: %u\t",
dtoh32(stats.pcie.num_rxcmplt),
dtoh32(stats.pcie.num_rxcmplt_drbl));
if (stats.pcie.num_rxcmplt_drbl)
printf("Avg per rx_cmplt drbl: %d.%d\n",
DIV_QUO(dtoh32(stats.pcie.num_rxcmplt),
dtoh32(stats.pcie.num_rxcmplt_drbl)),
DIV_REM(dtoh32(stats.pcie.num_rxcmplt),
dtoh32(stats.pcie.num_rxcmplt_drbl)));
else
printf("Avg per rx_cmplt drbl: 0.0\n");
printf(" Tx Cmplt:\tCount: %u\ttx_cmplt drbl: %u\t",
dtoh32(stats.pcie.num_txstatus),
dtoh32(stats.pcie.num_txstatus_drbl));
if (stats.pcie.num_txstatus_drbl)
printf("Avg per tx_cmplt drbl: %d.%d\n",
DIV_QUO(dtoh32(stats.pcie.num_txstatus),
dtoh32(stats.pcie.num_txstatus_drbl)),
DIV_REM(dtoh32(stats.pcie.num_txstatus),
dtoh32(stats.pcie.num_txstatus_drbl)));
else
printf("Avg per tx_cmplt drbl: 0.0\n");
}
break;
case WL_PWRSTATS_TYPE_SDIO:
{
wl_pwr_sdio_stats_t stats;
if (taglen < sizeof(wl_pwr_sdio_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_sdio_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
printf("SDIO:\n"
" active duration: %u\n"
" wakehost count: %u\n"
" intr count: data: %u\tmailbox: %u\terror: %u\n"
" ds_wake ON: count: %u\t duration: %u\n"
" ds_wake OFF: count: %u\t duration: %u\n"
" ds_d0 count: %u\t duration: %u\n"
" ds_d3 count: %u\t duration: %u\n"
" DEV_WAKE count: ASSRT: %u\tDASST: %u\n"
" ds mb rx count: DSACK: %u\tDSNACK: %u\tD3INFORM: %u\n"
" ds mb tx count: DSREQ: %u\tDSEXIT: %u\tD3ACK: %u\tD3EXIT: %u\n",
dtoh32(stats.sdio.active_dur),
dtoh32(stats.sdio.wakehost_cnt),
dtoh32(stats.sdio.data_intr_cnt),
dtoh32(stats.sdio.mb_intr_cnt),
dtoh32(stats.sdio.error_intr_cnt),
dtoh32(stats.sdio.ds_wake_on_cnt),
dtoh32(stats.sdio.ds_wake_on_dur),
dtoh32(stats.sdio.ds_wake_off_cnt),
dtoh32(stats.sdio.ds_wake_off_dur),
dtoh32(stats.sdio.ds_d0_cnt),
dtoh32(stats.sdio.ds_d0_dur),
dtoh32(stats.sdio.ds_d3_cnt),
dtoh32(stats.sdio.ds_d3_dur),
dtoh32(stats.sdio.ds_dw_assrt_cnt),
dtoh32(stats.sdio.ds_dw_dassrt_cnt),
dtoh32(stats.sdio.ds_rx_dsack_cnt),
dtoh32(stats.sdio.ds_rx_dsnack_cnt),
dtoh32(stats.sdio.ds_rx_d3inform_cnt),
dtoh32(stats.sdio.ds_tx_dsreq_cnt),
dtoh32(stats.sdio.ds_tx_dsexit_cnt),
dtoh32(stats.sdio.ds_tx_d3ack_cnt),
dtoh32(stats.sdio.ds_tx_d3exit_cnt));
}
break;
case WL_PWRSTATS_TYPE_PM_AWAKE1:
{
wl_pwr_pm_awake_stats_v1_t stats;
bool skip = FALSE;
uint32 dur_time, bits;
uint endidx;
if (taglen < sizeof(wl_pwr_pm_awake_stats_v1_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_pm_awake_stats_v1_t));
rc = BCME_ERROR;
break;
}
memcpy(&stats, ptr, taglen);
printf("PM WAKE:\n"
" Current Time: %u\n"
" HW MACC: 0x%08x\n"
" SW MACC: 0x%08x\n"
" PM Dur: %u\n"
" MPC Dur: %u\n"
" TSF Drift (Last/Min/Max/Avg/Cnt): %d/%d/%d/%u/%u\n"
" Frts (end_cnt/dur): %u/%u\n",
dtoh32(stats.awake_data.curr_time),
dtoh32(stats.awake_data.hw_macc),
dtoh32(stats.awake_data.sw_macc),
dtoh32(stats.awake_data.pm_dur),
dtoh32(stats.awake_data.mpc_dur),
dtoh32(stats.awake_data.last_drift),
dtoh32(stats.awake_data.min_drift),
dtoh32(stats.awake_data.max_drift),
dtoh32(stats.awake_data.avg_drift),
dtoh32(stats.awake_data.drift_cnt),
dtoh32(stats.frts_end_cnt),
dtoh32(stats.frts_time));
printf("\n");
i = endidx = stats.awake_data.pmwake_idx;
if (endidx > WLC_STA_AWAKE_STATES_MAX_V1) {
fprintf(stderr, "Unexpected idx %d > %d\n",
endidx, WLC_STA_AWAKE_STATES_MAX_V1);
rc = BCME_ERROR;
break;
}
if (endidx == 0)
endidx = WLC_STA_AWAKE_STATES_MAX_V1;
do {
if (i >= WLC_STA_AWAKE_STATES_MAX_V1)
i = 0;
dur_time = dtoh32(stats.awake_data.pm_state[i].timestamp);
bits = dtoh32(stats.awake_data.pm_state[i].reason);
if (dur_time == 0 && bits == 0)
continue;
printf(" State: %2d reason: 0x%04x time: %u\n",
i, bits, dur_time);
} while (++i != endidx);
printf("\n");
for (i = 0; i < WLC_PMD_EVENT_MAX_V1; i++) {
dur_time = dtoh32(stats.awake_data.pmd_event_wake_dur[i]);
if (dur_time == 0) {
if (i != 0)
skip = TRUE;
continue;
}
if (skip) {
printf(" ---\n");
skip = FALSE;
}
printf(" Event: %2d Wake-Duration: %u\n", i, dur_time);
}
}
break;
case WL_PWRSTATS_TYPE_PM_AWAKE2:
{
wl_pwr_pm_awake_stats_v2_t *stats = (wl_pwr_pm_awake_stats_v2_t *)ptr;
bool skip = FALSE;
uint32 dur_time, bits;
uint endidx;
if (taglen < sizeof(wl_pwr_pm_awake_stats_v2_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_pm_awake_stats_v2_t));
rc = BCME_ERROR;
break;
}
printf("PM WAKE:\n"
" Current Time: %u\n"
" HW MACC: 0x%08x\n"
" SW MACC: 0x%08x\n"
" PM Dur: %u\n"
" MPC Dur: %u\n"
" TSF Drift (Last/Min/Max/Avg/Cnt): %d/%d/%d/%u/%u\n"
" Frts (end_cnt/dur): %u/%u\n"
" Flags: %d, ASLEEP=%s\n",
dtoh32(stats->awake_data.curr_time),
dtoh32(stats->awake_data.hw_macc),
dtoh32(stats->awake_data.sw_macc),
dtoh32(stats->awake_data.pm_dur),
dtoh32(stats->awake_data.mpc_dur),
dtoh32(stats->awake_data.last_drift),
dtoh32(stats->awake_data.min_drift),
dtoh32(stats->awake_data.max_drift),
dtoh32(stats->awake_data.avg_drift),
dtoh32(stats->awake_data.drift_cnt),
dtoh32(stats->awake_data.frts_end_cnt),
dtoh32(stats->awake_data.frts_time),
stats->awake_data.flags,
((stats->awake_data.flags &
WL_PWR_PM_AWAKE_STATS_WAKE_MASK) == WL_PWR_PM_AWAKE_STATS_ASLEEP) ?
"True":"False");
printf("\n");
i = endidx = stats->awake_data.pmwake_idx;
if (endidx > stats->awake_data.pm_state_len) {
fprintf(stderr, "Unexpected idx %d > %d\n",
endidx, stats->awake_data.pm_state_len);
rc = BCME_ERROR;
break;
}
if (endidx == 0)
endidx = stats->awake_data.pm_state_len;
do {
wlc_pm_debug_t *pm_state = (wlc_pm_debug_t *)
(((uint8 *)&stats->awake_data) +
stats->awake_data.pm_state_offset);
if (i >= stats->awake_data.pm_state_len)
i = 0;
dur_time = dtoh32(pm_state[i].timestamp);
bits = dtoh32(pm_state[i].reason);
if (dur_time == 0 && bits == 0)
continue;
printf(" State: %2d reason: 0x%04x time: %u\n",
i, bits, dur_time);
} while (++i != endidx);
printf("\n");
for (i = 0; i < stats->awake_data.pmd_event_wake_dur_len; i++) {
uint32 *pmd_event_wake_dur = (uint32 *)
(((uint8 *)&stats->awake_data) +
stats->awake_data.pmd_event_wake_dur_offset);
dur_time = dtoh32(pmd_event_wake_dur[i]);
if (dur_time == 0) {
if (i != 0)
skip = TRUE;
continue;
}
if (skip) {
printf(" ---\n");
skip = FALSE;
}
printf(" Event: %2d Wake-Duration: %u\n", i, dur_time);
}
}
break;
case WL_PWRSTATS_TYPE_CONNECTION:
{
wl_pwr_connect_stats_t connect_stats;
if (taglen < sizeof(wl_pwr_connect_stats_t)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen, (int)sizeof(wl_pwr_connect_stats_t));
rc = BCME_ERROR;
break;
}
memcpy(&connect_stats, ptr, taglen);
printf("Connect:\n"
" Count: %u\n"
" Duration: %u\n",
dtoh32(connect_stats.count),
dtoh32(connect_stats.dur));
}
break;
case WL_PWRSTATS_TYPE_MIMO_PS_METRICS:
{
wl_mimo_meas_metrics_t stats;
bool ocl_stats = FALSE;
if (taglen < STRUCT_SIZE_THROUGH(&stats, total_tx_time_3chain)) {
fprintf(stderr, "Short len for %d: %d < %d\n",
type, taglen,
(int)STRUCT_SIZE_THROUGH(&stats, total_tx_time_3chain));
rc = BCME_ERROR;
break;
} else if (taglen >= STRUCT_SIZE_THROUGH(&stats, total_rx_time_ocl)) {
ocl_stats = TRUE;
}
memcpy(&stats, ptr, taglen);
printf("MIMO ps measurement metrics:\n");
if (ocl_stats) {
printf(" MIMO Idle duration:\t%10u (OCL Idle: %10u)\n"
" OCL Idle duration:\t%10u\n",
dtoh32(stats.total_idle_time_mimo),
dtoh32(stats.total_idle_time_ocl),
dtoh32(stats.total_idle_time_ocl));
} else {
printf(" MIMO Idle duration:\t%10u\n",
dtoh32(stats.total_idle_time_mimo));
}
printf(" SISO Idle duration:\t%10u\n",
dtoh32(stats.total_idle_time_siso));
if (ocl_stats) {
printf(" MIMO Rx duration:\t%10u (OCL Rx: %10u)\n"
" OCL Rx duration:\t%10u\n",
dtoh32(stats.total_rx_time_mimo),
dtoh32(stats.total_rx_time_ocl),
dtoh32(stats.total_rx_time_ocl));
} else {
printf(" MIMO Rx duration:\t%10u\n",
dtoh32(stats.total_rx_time_mimo));
}
printf(" SISO RX duration:\t%10u\n"
" TX 1-chain duration:\t%10u\n"
" TX 2-chain duration:\t%10u\n"
" TX 3-chain duration:\t%10u\n",
dtoh32(stats.total_rx_time_siso),
dtoh32(stats.total_tx_time_1chain),
dtoh32(stats.total_tx_time_2chain),
dtoh32(stats.total_tx_time_3chain));
}
break;
default:
printf("Skipping uknown %d-byte tag %d\n", taglen, type);
break;
}
printf("\n");
if (rc)
break;
/* Adjust length to account for padding, but don't exceed total len */
taglen = (ROUNDUP(taglen, 4) > len) ? len : ROUNDUP(taglen, 4);
len -= taglen;
}
if (len && (len < 2 * sizeof(uint16))) {
fprintf(stderr, "Invalid length remaining %d\n", len);
rc = BCME_ERROR;
}
return (rc);
}
/*
* Function to output heap details for wl memuse command
*/
static int
wl_memuse(void *wl, cmd_t *cmd, char **argv)
{
int err;
memuse_info_t *mu;
wlc_rev_info_t revinfo;
memset(&revinfo, 0, sizeof(revinfo));
if ((err = wlu_get(wl, WLC_GET_REVINFO, &revinfo, sizeof(revinfo))) < 0)
return err;
if (revinfo.driverrev && dtoh32(revinfo.driverrev) < DINGO_FW_REV) {
/* memuse implementation for FW branch version < 9.0 (before Dingo) */
wl_varstr(wl, cmd, argv);
}
else {
/* memuse implementation for FW branch version > 9.0 (Dingo and above) */
memset(buf, 0, sizeof(memuse_info_t));
strcpy(buf, cmd->name);
if ((err = wlu_get(wl, WLC_GET_VAR, &buf[0], sizeof(memuse_info_t))) < 0) {
return err;
}
mu = (memuse_info_t *)buf;
if (mu->ver == RTE_MEMUSEINFO_VER && mu->len >= sizeof(memuse_info_t)) {
printf("Heap Total: %d(%dK)\n", mu->arena_size, KB(mu->arena_size));
printf("Free: %d(%dK), LWM: %d(%dK)\n",
mu->arena_free, KB(mu->arena_free),
mu->free_lwm, KB(mu->free_lwm));
printf("In use: %d(%dK), HWM: %d(%dK)\n",
mu->inuse_size, KB(mu->inuse_size),
mu->inuse_hwm, KB(mu->inuse_hwm));
printf("Malloc failure count: %d\n", mu->mf_count);
} else {
printf("Heap Total: %d(%dK), Heap Free: %d(%dK)\n",
mu->arena_size, KB(mu->arena_size),
mu->arena_free, KB(mu->arena_free));
}
}
return (0);
}
/* 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);
seq_list_len = bufp - buf;
while ((seq_list_len < MEMBLOCK) && (next_cmd != NULL)) {
/* note, driver handles the IOCTL buffer 32-bit alignment */
len = next_cmd->cmd_header.len;
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;
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;
}
}
/* 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. */
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;
bool immed_flag = FALSE;
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);
if (strcmp(*argv, "immediate") == 0) {
immed_flag = TRUE;
argv++;
}
mkeep_alive_pkt.period_msec = strtoul(*argv, NULL, 0);
if (mkeep_alive_pkt.period_msec & WL_MKEEP_ALIVE_IMMEDIATE) {
fprintf(stderr, "Period %d too large\n", mkeep_alive_pkt.period_msec);
return -1;
}
if (immed_flag && mkeep_alive_pkt.period_msec) {
mkeep_alive_pkt.period_msec |= WL_MKEEP_ALIVE_IMMEDIATE;
}
mkeep_alive_pkt.period_msec = htod32(mkeep_alive_pkt.period_msec);
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);
}
#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_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"},
{"BONAIRE SAINT EUSTATIUS AND SABA", "BQ"},
{"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"},
{"CURACAO", "CW"},
{"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"},
{"SAINT MARTIN", "MF"},
{"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 int
wl_ptk_start(void *wl, cmd_t *cmd, char **argv)
{
const char *str;
int buf_len;
int str_len;
int rc;
int count;
struct wl_ptk_start_iov_v1 *ptk_start_args;
UNUSED_PARAMETER(cmd);
count = ARGCNT(argv);
/*
** Set the attributes.
*/
if (count < 5) {
fprintf(stderr, "<sec-type> <peer_addr> <pmk> <role> expected\n");
rc = -1;
goto exit;
}
str = "ptk_start";
str_len = strlen(str);
strncpy(buf, str, str_len);
buf[ str_len ] = '\0';
ptk_start_args = (struct wl_ptk_start_iov_v1 *) (buf + str_len + 1);
ptk_start_args->sec_type = strtoul(*(argv + 1), NULL, 0); /* WL_PTK_START_SEC_TYPE_PMK */
if (!wl_ether_atoe(*(argv + 2), &ptk_start_args->peer_addr)) {
fprintf(stderr, "awdl ranging config mac addr err\n");
rc = -1;
goto exit;
}
ptk_start_args->tlvs[0].id = WL_PTK_START_TLV_PMK;
ptk_start_args->tlvs[0].len = strlen(*(argv+3));
strncpy((char*)ptk_start_args->tlvs[0].data, *(argv+3), ptk_start_args->tlvs[0].len);
printf("PMK[len = %d]: %s\n", ptk_start_args->tlvs[0].len,
(char*)ptk_start_args->tlvs[0].data);
ptk_start_args->role = strtoul(*(argv + 4), NULL, 0); /* WL_PTK_START_SEC_TYPE_PMK */
buf_len = str_len + 1 + sizeof(struct wl_ptk_start_iov_v1) + ptk_start_args->tlvs[0].len;
rc = wlu_set(wl,
WLC_SET_VAR,
buf,
buf_len);
exit:
return (rc);
}
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(" : ");
/* std MCS 0-9 and prop MCS 10-11 */
for (j = 0; j <= 11; j++)
if (isbitset(mcsset[i], j))
printf("%dx%d ", j, i + 1);
printf("\n");
} else {
break;
}
}
}
static void
wl_print_txbf_mcsset(char *mcsset, char *prefix)
{
int i;
printf("%s MCS : [ ", prefix);
for (i = 0; i < (TXBF_RATE_MCS_ALL * 8); i++)
if (isset(mcsset, i))
printf("%d ", i);
printf("]\n");
}
static void
wl_print_txbf_vhtmcsset(uint16 *mcsset, char *prefix)
{
int i, j;
for (i = 0; i < TXBF_RATE_VHT_ALL; i++) {
if (mcsset[i]) {
if (i == 0)
printf("%s VHT : ", prefix);
else
printf(" : ");
for (j = 0; j <= 11; j++)
if (isbitset(mcsset[i], j))
printf("%dx%d ", j, i + 1);
printf("\n");
} else {
break;
}
}
}
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;
}
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 void
free_cca_array(cca_congest_channel_req_t **favg, int favg_chan_elts)
{
int i;
if (favg == NULL)
{
return;
}
for (i = 0; i < favg_chan_elts; i++) {
if (favg[i] != NULL) {
free(favg[i]);
favg[i] = NULL;
}
}
if (favg != NULL) {
free(favg);
favg = NULL;
}
}
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 = NULL, **new_avg = NULL; /* 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_individ = FALSE;
bool do_analyze = TRUE;
bool curband = FALSE;
int avg_chan_idx = 0, avg_chan_elts = 40;
uint32 flags;
int j;
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 (tmp_channel == 0) {
/* do all channels */
base = 1; limit = MAXCHANNEL;
} 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;
}
avg = (cca_congest_channel_req_t **)
calloc(1, sizeof(cca_congest_channel_req_t*) * avg_chan_elts);
if (avg == NULL) {
printf("unable to allocate memory\n");
return BCME_NOMEM;
}
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) {
goto func_exit;
}
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);
err = -1;
goto func_exit;
}
/* Summarize and save summary for this channel */
if (avg_chan_idx >= avg_chan_elts) {
new_avg = (cca_congest_channel_req_t **)calloc
(1, sizeof(cca_congest_channel_req_t*) * (avg_chan_elts + 10));
if (new_avg == NULL) {
printf("unable to allocate memory\n");
err = BCME_NOMEM;
goto func_exit;
}
memcpy(new_avg, avg, avg_chan_elts);
free_cca_array(avg, avg_chan_elts);
avg_chan_elts += 10;
avg = new_avg;
new_avg = NULL;
}
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");
err = BCME_NOMEM;
goto func_exit;
}
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 */
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) {
goto func_exit;
}
if ((err = wlu_iovar_getint(wl, "chanspec", (int *)&val)) < 0) {
printf("CCA: Can't get currrent chanspec\n");
goto func_exit;
}
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) {
if (err > 0) {
printf("Cannot find a good channel due to: %s\n", cca_errors[err]);
err = BCME_ERROR;
}
goto func_exit;
}
printf("Recommended channel: %d\n", wf_chspec_ctlchan(new_chanspec));
func_exit:
free_cca_array(avg, avg_chan_elts);
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 void
dlystat_dump(txdelay_stats_t *txdelay_stats)
{
const char *const aci_names[] = { "AC_BE", "AC_BK", "AC_VI", "AC_VO"};
uint32 i, last, s;
uint ac;
uint32 sum;
uint32 max_val, total = 0;
scb_delay_stats_t *delay_stats;
struct ether_addr *ea;
uint32 dlystat_cnt = txdelay_stats->scb_cnt;
if (txdelay_stats->full_result == TXDELAY_STATS_PARTIAL_RESULT) {
printf("\tThis is partial results for SCBs\n"
"\tcaused by insuffient IOCTL buffer\n"
"\tIf you want to get full SCBs\n"
"\tPlease use iteration for each of specific mac_address\n"
"\twhich can be got from other iovars such as assoclist\n");
}
for (s = 0; s < dlystat_cnt; s++) {
ea = &txdelay_stats->scb_delay_stats[s].ea;
printf("[SCB%d]\n\t%s\n", s, wl_ether_etoa(ea));
/* Report Latency and packet loss statistics (for each AC) */
for (ac = 0; ac < AC_COUNT; ac++) {
delay_stats = txdelay_stats->scb_delay_stats[s].dlystats + ac;
for (i = 0, total = 0, sum = 0; i < SCB_RETRY_SHORT_DEF; i++) {
total += delay_stats->txmpdu_cnt[i];
sum += delay_stats->delay_sum[i];
}
if (total) {
printf("%s: PLoss %d/%d (%d%%)\n",
aci_names[ac], delay_stats->txmpdu_lost, total,
(delay_stats->txmpdu_lost * 100) / total);
printf("\tDelay stats in usec (avg/min/max): %d %d %d\n",
CEIL(sum, total), delay_stats->delay_min,
delay_stats->delay_max);
printf("\tTxcnt Hist :");
for (i = 0; i < SCB_RETRY_SHORT_DEF; i++) {
printf(" %d", delay_stats->txmpdu_cnt[i]);
}
printf("\n\tDelay vs Txcnt:");
for (i = 0; i < SCB_RETRY_SHORT_DEF; i++) {
if (delay_stats->txmpdu_cnt[i])
printf(" %d", delay_stats->delay_sum[i] /
delay_stats->txmpdu_cnt[i]);
else
printf(" -1");
}
printf("\n");
for (i = 0, max_val = 0, last = 0; i < WLPKTDLY_HIST_NBINS; i++) {
max_val += delay_stats->delay_hist[i];
if (delay_stats->delay_hist[i]) last = i;
}
last = 8 * (last/8 + 1) - 1;
if (max_val) {
printf("\tDelay Hist \n\t\t:");
for (i = 0; i <= last; i++) {
printf(" %d(%d%%)", delay_stats->
delay_hist[i],
(delay_stats->delay_hist[i] * 100)
/ max_val);
if ((i % 8) == 7 && i != last)
printf("\n\t\t:");
}
}
printf("\n");
}
}
printf("\n");
}
}
static int
wl_dlystats(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
txdelay_stats_t *dlystats_param;
txdelay_stats_t *delay_stats;
dlystats_param = (txdelay_stats_t *)malloc(sizeof(*dlystats_param));
if (dlystats_param == NULL) {
return BCME_NOMEM;
}
memset(dlystats_param, 0, sizeof(*dlystats_param));
dlystats_param->version = TXDELAY_STATS_VERSION;
if (*++argv) {
if (!wl_ether_atoe(*argv, &dlystats_param->scb_delay_stats[0].ea)) {
ret = BCME_USAGE_ERROR;
goto EXIT;
}
dlystats_param->scb_cnt = 1;
} else {
dlystats_param->scb_cnt = MAX_TXDELAY_STATS_SCBS;
}
ret = wlu_iovar_getbuf(wl, cmd->name, dlystats_param, sizeof(*dlystats_param),
buf, WLC_IOCTL_MAXLEN);
if (ret) {
if (ret == BCME_VERSION) {
printf("txdelay_stats_t version is mismatched\n");
}
goto EXIT;
}
delay_stats = (txdelay_stats_t *)buf;
dlystat_dump(delay_stats);
EXIT:
free(dlystats_param);
return ret;
}
static int
wl_dlystats_clear(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
UNUSED_PARAMETER(argv);
err = wlu_iovar_set(wl, cmd->name, buf, 0);
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);
}
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_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_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, 0, sizeof(wl_el_set_params_t));
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, 0, sizeof(wl_el_set_params_t));
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) || (argc > 3)) {
return BCME_USAGE_ERROR;
}
memset(&pars, 0, sizeof(wl_el_set_params_t));
pars.set = atoi(argv[1]);
if (argc == 3)
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_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, 0, sizeof(wl_el_tag_params_t));
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(pars));
return err;
}
static int wl_event_log_get(void *wl, cmd_t *cmd, char **argv)
{
const char *cmdname = "event_log_get";
char *set_arg = NULL;
wl_el_set_params_t pars;
int argc;
int err = BCME_OK;
UNUSED_PARAMETER(cmd);
/* arg count */
for (argv++, argc = 0; argv[argc]; argc++);
memset(&pars, 0, sizeof(wl_el_set_params_t));
/* Look for -f */
if ((*argv) && !strcmp(*argv, "-f")) {
set_arg = *++argv;
if (!set_arg)
{
fprintf(stderr, "Missing argument to -f option\n");
return BCME_USAGE_ERROR;
}
/* Ignore other arguments */
pars.set = atoi(set_arg);
pars.size = 0;
/* cause a flush of an event log buffer associated with this particular
* set. This flush is caused by trigerring logtrace
*/
err = wlu_iovar_set(wl, cmdname, &pars, sizeof(wl_el_set_params_t));
}
else
{
fprintf(stderr, "Missing -f option. Currently -g <set> -s <buf_len> "
"options are not supported\n");
err = BCME_USAGE_ERROR;
}
return err;
}
#ifdef SERDOWNLOAD
static int dhd_rwl_download(void *dhd, char *fwname, char *command);
int
rwl_download(void *dhd, cmd_t *cmd, char **argv)
{
char *fname = NULL;
char *vname = NULL;
int ret = 0;
UNUSED_PARAMETER(cmd);
if (!*++argv) {
fprintf(stderr, "Require dongle image filename \n");
ret = -1;
goto exit;
}
else {
fname = *argv;
if (debug)
printf("dongle image file is %s\n", fname);
}
if (!*++argv) {
fprintf(stderr, "vars filename missing, assuming no var file\n");
ret = -1;
goto exit;
}
else {
vname = *argv;
if (debug)
printf("dongle var file is %s\n", vname);
}
ret = dhd_rwl_download(dhd, fname, "download");
printf("ret = %d\n", ret);
if (ret == 0)
ret = dhd_rwl_download(dhd, vname, "nvdownload");
exit:
return ret;
}
static int
dhd_rwl_download(void *dhd, char *fwfile, char *command)
{
unsigned char *bufp = NULL;
int len = 0, length = 0;
int ret = 0;
char *p;
unsigned char *buff = NULL;
int remained_bytes;
uint32 start = 0;
FILE *fp = NULL;
struct stat finfo;
unsigned long status;
/* Open the firmware file */
if (!(fp = fopen(fwfile, "rb"))) {
perror(fwfile);
ret = -1;
goto exit;
}
if (stat(fwfile, &finfo)) {
printf("dhd_rwl_download: %s: %s\n", fwfile, strerror(errno));
ret = -1;
goto exit;
}
length = finfo.st_size;
if (length <= 0) {
ret = -1;
goto exit;
}
if ((bufp = malloc(length +4)) == NULL) {
printf("dhd_rwl_download: Unable to allowcate %d bytes!\n", len);
ret = -1;
goto exit;
}
/* Read the firmware file into the buffer */
status = fread(bufp, sizeof(uint8), length, fp);
/* close the firmware file */
fclose(fp);
fp = NULL;
if ((int)status < length) {
printf("dhd_rwl_download: Short read in %s!\n", fwfile);
ret = -1;
goto exit;
}
printf("Starting %s, total file length is %d\n", command, length);
/* do the download reset */
if ((ret = wlu_iovar_setint(dhd, command, TRUE))) {
fprintf(stderr, "dhd_rwl_download: failed to put dongle to download mode\n");
ret = -1;
goto exit;
}
buff = bufp;
remained_bytes = len = length;
while (remained_bytes > 0) {
printf(".");
p = buf;
memset(p, 0, WLC_IOCTL_MAXLEN);
strcpy(p, "membytes");
p += strlen("membytes") + 1;
memcpy(p, &start, sizeof(int));
p += sizeof(int);
if (remained_bytes >= MEMBLOCK)
len = MEMBLOCK;
else
len = remained_bytes;
memcpy(p, &len, sizeof(int));
p += sizeof(int);
memcpy(p, buff, len);
p += len;
if (debug) {
printf("sending %d bytes block: \n", (int)(p - buf));
}
ret = wlu_set(dhd, WLC_SET_VAR, &buf[0], (int) (p - buf));
if (ret) {
fprintf(stderr, "%s: error %d on writing %d membytes at 0x%08x\n",
"wl_set()", ret, len, start);
goto exit;
}
start += len;
buff += len;
remained_bytes -= len;
}
printf("\n");
/* start running the downloaded code, download complete */
if ((ret = wlu_iovar_setint(dhd, command, FALSE))) {
fprintf(stderr, "dhd_rwl_download: failed to take dongle out of download mode\n");
goto exit;
}
exit:
if (bufp)
free(bufp);
if (fp)
fclose(fp);
return ret;
}
/* Check that strlen("membytes")+1 + 2*sizeof(int32) + MEMBLOCK <= WLC_IOCTL_MAXLEN */
#if (MEMBLOCK + 17 > WLC_IOCTL_MAXLEN)
#error MEMBLOCK/WLC_IOCTL_MAXLEN sizing
#endif
static int dhd_hsic_download(void *dhd, char *fwname, char *nvname);
static int ReadFiles(char *fwfile, char *nvfile, unsigned char ** buffer);
static int check_file(unsigned char *headers);
static char* chip_select = "none";
int
dhd_init(void *dhd, cmd_t *cmd, char **argv)
{
int ret = -1;
UNUSED_PARAMETER(cmd);
if (!*++argv) {
fprintf(stderr, "Error: Missing require chip ID"
"<4325, 4329, 43291, 4330a1, 4330 or hsic>\n");
ret = BCME_USAGE_ERROR;
}
else if (strcmp(*argv, "4325") && strcmp(*argv, "4329") &&
strcmp(*argv, "43291") && strcmp(*argv, "4330") &&
strcmp(*argv, "4330a1") && strcmp(*argv, "hsic")) {
fprintf(stderr, "Error: Unsupported chip ID %s\n", *argv);
ret = BCME_BADARG;
}
else if ((ret = wlu_iovar_setbuf(dhd, "init", *argv, strlen(*argv) + 1,
buf, WLC_IOCTL_MAXLEN))) {
fprintf(stderr, "Error: %s: failed to initialize the dongle \n",
"dhd_init()");
}
else
ret = 0;
if (ret == 0) {
if (!strcmp(*argv, "4325")) {
fprintf(stdout, "4325 is the selected chip id\n");
chip_select = "4325";
} else if (!strcmp(*argv, "4329")) {
fprintf(stdout, "4329 is the selected chip id\n");
chip_select = "4329";
} else if (!strcmp(*argv, "43291")) {
fprintf(stdout, "43291 is the selected chip id\n");
chip_select = "43291";
} else if (!strcmp(*argv, "4330")) {
fprintf(stdout, "4330b0 is the selected chip id\n");
chip_select = "4330b0";
} else if (!strcmp(*argv, "4330a1")) {
fprintf(stdout, "4330a1 is the selected chip id\n");
chip_select = "4330a1";
} else if (!strcmp(*argv, "hsic")) {
fprintf(stdout, "hsic interface is selected\n");
chip_select = "hsic";
} else
chip_select = "none";
}
return ret;
}
int
dhd_download(void *dhd, cmd_t *cmd, char **argv)
{
char *fname = NULL;
char *vname = NULL;
uint32 start = 0;
uint32 last4bytes;
int ret = 0;
uint file_size;
int ram_size = 0, var_size, var_words, nvram_len, remained_bytes;
FILE *fp = NULL;
struct stat finfo;
char *bufp;
int len;
uint8 memblock[MEMBLOCK];
uint8 varbuf[WLC_IOCTL_MAXLEN];
UNUSED_PARAMETER(cmd);
if (!strcmp(chip_select, "none")) {
fprintf(stderr, "chip init must be called before firmware download. \n");
ret = BCME_USAGE_ERROR;
goto exit;
}
if (!strcmp(chip_select, "4325")) {
fprintf(stdout, "using 4325 ram_info\n");
ram_size = RAM_SIZE_4325;
} else if (!strcmp(chip_select, "4329")) {
fprintf(stdout, "using 4329 ram_info\n");
ram_size = RAM_SIZE_4329;
} else if (!strcmp(chip_select, "43291")) {
fprintf(stdout, "using 43291 ram_info\n");
ram_size = RAM_SIZE_43291;
} else if (!strcmp(chip_select, "4330b0")) {
fprintf(stdout, "using 4330 b0 ram_info\n");
ram_size = RAM_SIZE_4330_b0;
} else if (!strcmp(chip_select, "4330a1")) {
fprintf(stdout, "using 4330a1 ram_info\n");
ram_size = RAM_SIZE_4330_a1;
} else if (!strcmp(chip_select, "hsic")) {
fprintf(stdout, "using hsic interface\n");
} else {
fprintf(stderr, "Error: unknown chip\n");
ret = BCME_USAGE_ERROR;
goto exit;
}
if (!*++argv) {
fprintf(stderr, "Require dongle image filename \n");
ret = BCME_USAGE_ERROR;
goto exit;
}
else {
fname = *argv;
if (debug)
printf("dongle image file is %s\n", fname);
}
if (!*++argv) {
fprintf(stderr, "vars filename missing, assuming no var file\n");
ret = BCME_USAGE_ERROR;
goto exit;
}
else {
vname = *argv;
if (debug)
printf("dongle var file is %s\n", vname);
}
/* do the download on hsic */
/* merge the firmware and the nvram */
if (!strcmp(chip_select, "hsic")) {
/* the hsic firwmare and download code */
ret = dhd_hsic_download(dhd, fname, vname);
return ret;
}
if (!(fp = fopen(fname, "rb"))) {
perror(fname);
ret = BCME_BADARG;
goto exit;
}
if (stat(fname, &finfo)) {
perror(fname);
ret = -1;
goto exit;
}
file_size = finfo.st_size;
if (debug) {
printf("%s file_size=%d\n", fname, file_size);
}
/* do the download reset if not suppressed */
if ((ret = wlu_iovar_setint(dhd, "download", TRUE))) {
fprintf(stderr, "%s: failed to put dongle in download mode\n",
"dhd_iovar_setint()");
goto exit;
}
memset(memblock, 0, MEMBLOCK);
printf("downloading %s, file_size=%d\n", fname, file_size);
/* read the file and push blocks down to memory */
while ((len = fread(memblock, sizeof(uint8), MEMBLOCK, fp))) {
if (len < MEMBLOCK && !feof(fp)) {
fprintf(stderr, "%s: error reading file %s\n", "fread()", fname);
ret = -1;
goto exit;
}
if (debug) {
printf("memblock=\n%s\n", memblock);
}
bufp = buf;
memset(bufp, 0, WLC_IOCTL_MAXLEN);
strcpy(bufp, "membytes");
bufp += strlen("membytes") + 1;
memcpy(bufp, &start, sizeof(int));
bufp += sizeof(int);
memcpy(bufp, &len, sizeof(int));
bufp += sizeof(int);
memcpy(bufp, memblock, len);
ret = wl_set(dhd, WLC_SET_VAR, &buf[0], (bufp - buf + len));
if (ret) {
fprintf(stderr, "%s: error %d on writing %d membytes at 0x%08x\n",
"wl_set()", ret, len, start);
goto exit;
}
start += len;
memset(memblock, 0, MEMBLOCK);
}
if (!feof(fp)) {
fprintf(stderr, "%s: error reading file %s\n", "feof()", fname);
ret = -1;
goto exit;
}
fclose(fp);
fp = NULL;
if (vname) {
/* download the vars file if specified */
/* read in the file */
if (!(fp = fopen(vname, "rb"))) {
perror(vname);
ret = BCME_BADARG;
goto exit;
}
if (stat(vname, &finfo)) {
perror(vname);
ret = -1;
goto exit;
}
file_size = finfo.st_size;
printf("downloading %s, file_size=%d\n", vname, file_size);
memset(varbuf, 0, WLC_IOCTL_MAXLEN);
/* read the file and push blocks down to memory */
if (fread(varbuf, 1, file_size, fp) != file_size) {
perror(fname);
ret = -1;
goto exit;
}
fclose(fp);
fp = NULL;
if (debug) {
printf("the original varbuf=%s\n", varbuf);
}
/* convert linefeeds to nuls */
nvram_len = process_nvram_vars((char*)&varbuf, file_size);
if (debug) {
printf("after process_nvram_vars(), %s nvram_len=%d\n%s\n",
vname, nvram_len, varbuf);
}
bufp = (char*)&varbuf + nvram_len;
*bufp++ = 0;
var_size = ROUNDUP(nvram_len + 1, 4);
/* calculate start address */
start = ram_size - var_size - 4;
if (debug) {
printf("var_size=%d, start=0x%0X\n", var_size, start);
}
/* need to send the last 4 bytes. */
var_words = var_size / 4;
last4bytes = (~var_words << 16) | (var_words & 0x0000FFFF);
last4bytes = htol32(last4bytes);
if (debug) {
printf("last4bytes=0x%0X\n", last4bytes);
}
bufp = (char*)&varbuf + var_size;
memcpy(bufp, &last4bytes, 4);
/* send down var_size+4 bytes with each time "membytes" MEMBLOCK bytes */
bufp = (char*)&varbuf;
remained_bytes = var_size + 4;
while (remained_bytes > 0) {
char *p;
p = buf;
memset(p, 0, WLC_IOCTL_MAXLEN);
strcpy(p, "membytes");
p += strlen("membytes") + 1;
memcpy(p, &start, sizeof(int));
p += sizeof(int);
if (remained_bytes >= MEMBLOCK) {
len = MEMBLOCK;
}
else
len = remained_bytes;
memcpy(p, &len, sizeof(int));
p += sizeof(int);
memcpy(p, bufp, len);
p += len;
if (debug) {
printf("sending %d bytes block:\n", (int)(p - buf));
printf("%s\n", buf);
}
ret = wl_set(dhd, WLC_SET_VAR, &buf[0], (p - buf));
if (ret) {
fprintf(stderr, "%s: error %d on writing %d membytes at 0x%08x\n",
"wl_set()", ret, len, start);
goto exit;
}
start += len;
bufp += len;
remained_bytes -= len;
}
}
/* start running the downloaded code if not suppressed */
if ((ret = wlu_iovar_setint(dhd, "download", FALSE))) {
fprintf(stderr, "%s: failed to take dongle out of download mode\n",
"dhd_iovar_setint()");
goto exit;
}
exit:
if (fp)
fclose(fp);
return ret;
}
int
dhd_upload(void *dhd, cmd_t *cmd, char **argv)
{
char *fname = NULL;
uint32 start = 0;
uint32 size;
int ram_size;
FILE *fp = NULL;
uint len;
int ret = 0;
UNUSED_PARAMETER(cmd);
if (!strcmp(chip_select, "none")) {
fprintf(stderr, "chip init must be called before firmware download. \n");
ret = BCME_USAGE_ERROR;
goto exit;
}
if (!strcmp(chip_select, "4325")) {
fprintf(stdout, "using 4325 ram_info\n");
ram_size = RAM_SIZE_4325;
} else if (!strcmp(chip_select, "4329")) {
fprintf(stdout, "using 4329 ram_info\n");
ram_size = RAM_SIZE_4329;
} else if (!strcmp(chip_select, "43291")) {
fprintf(stdout, "using 43291 ram_info\n");
ram_size = RAM_SIZE_43291;
} else if (!strcmp(chip_select, "4330b0")) {
fprintf(stdout, "using 4330 b0 ram_info\n");
ram_size = RAM_SIZE_4330_b0;
} else if (!strcmp(chip_select, "4330a1")) {
fprintf(stdout, "using 4330 a1 ram_info\n");
ram_size = RAM_SIZE_4330_a1;
} else {
fprintf(stderr, "Error: unknown chip\n");
ret = BCME_USAGE_ERROR;
goto exit;
}
argv++;
if (debug) {
printf("argv=%s\n", *argv);
}
fname = *argv;
/* validate arguments */
if (!fname) {
fprintf(stderr, "filename required\n");
ret = BCME_BADARG;
goto exit;
}
if (!(fp = fopen(fname, "wb"))) {
perror(fname);
ret = BCME_BADARG;
goto exit;
}
/* default size to full RAM */
size = ram_size - start;
/* read memory and write to file */
while (size) {
char *ptr;
int params[2];
len = MIN(MEMBLOCK, size);
params[0] = start;
params[1] = len;
ret = wlu_iovar_getbuf(dhd, "membytes", params, 2 * sizeof(int),
(void**)&ptr, MEMBLOCK);
if (ret) {
fprintf(stderr, "dhd_upload(): failed reading %d membytes from 0x%08x\n",
len, start);
break;
}
if (fwrite(ptr, sizeof(*ptr), len, fp) != len) {
fprintf(stderr, "dhd_upload(): error writing to file %s\n", fname);
ret = -1;
break;
}
start += len;
size -= len;
}
fclose(fp);
exit:
return ret;
}
static int
dhd_hsic_download(void *dhd, char *fwfile, char *nvfile)
{
unsigned char *bufp = NULL;
int len = 0, length = 0;
int ret = 0;
char *p;
unsigned char *buff = NULL;
int remained_bytes;
uint32 start = 0;
/* read and merge fw and nvram files */
length = ReadFiles(fwfile, nvfile, &bufp);
if (length <= 0) {
ret = -1;
goto exit;
}
printf("Starting download, total file length is %d\n", length);
/* do the download reset */
if ((ret = wlu_iovar_setint(dhd, "download", TRUE))) {
fprintf(stderr, "dhd_hsic_download: failed to put dongle to download mode\n");
goto exit;
}
buff = bufp;
remained_bytes = len = length;
while (remained_bytes > 0) {
printf(".");
p = buf;
memset(p, 0, WLC_IOCTL_MAXLEN);
strcpy(p, "membytes");
p += strlen("membytes") + 1;
memcpy(p, &start, sizeof(int));
p += sizeof(int);
if (remained_bytes >= MEMBLOCK)
len = MEMBLOCK;
else
len = remained_bytes;
memcpy(p, &len, sizeof(int));
p += sizeof(int);
memcpy(p, buff, len);
p += len;
if (debug) {
printf("sending %d bytes block: \n", (int)(p - buf));
}
ret = wlu_set(dhd, WLC_SET_VAR, &buf[0], (p - buf));
if (ret) {
fprintf(stderr, "%s: error %d on writing %d membytes at 0x%08x\n",
"wl_set()", ret, len, start);
goto exit;
}
start += len;
buff += len;
remained_bytes -= len;
}
printf("\n");
/* start running the downloaded code, download complete */
if ((ret = wlu_iovar_setint(dhd, "download", FALSE))) {
fprintf(stderr, "dhd_hsic_download: failed to take dongle out of download mode\n");
goto exit;
}
exit:
if (bufp)
free(bufp);
return ret;
}
static int ReadFiles(char *fname, char *vname, unsigned char ** buffer)
{
FILE *fp = NULL;
FILE *fp1 = NULL;
struct stat finfo;
uint8 *buf = NULL;
int len, fwlen, actual_len, nvlen = 0;
struct trx_header *hdr;
unsigned long status;
unsigned int pad;
unsigned int i;
int ret = -1;
/* Open the firmware file */
if (!(fp = fopen(fname, "rb"))) {
perror(fname);
ret = BCME_BADARG;
goto exit;
}
if (stat(fname, &finfo)) {
printf("dhd_download: %s: %s\n", fname, strerror(errno));
ret = -1;
goto exit;
}
len = fwlen = finfo.st_size;
/* Open nvram file */
if (!(fp1 = fopen(vname, "rb"))) {
perror(fname);
ret = BCME_BADARG;
goto exit;
}
if (stat(vname, &finfo)) {
printf("dhd_download: %s: %s\n", vname, strerror(errno));
ret = -1;
goto exit;
}
nvlen = finfo.st_size;
len += nvlen;
if ((buf = malloc(len +4)) == NULL) {
printf("dhd_download: Unable to allowcate %d bytes!\n", len);
ret = BCME_NOMEM;
goto exit;
}
/* Read the firmware file into the buffer */
status = fread(buf, sizeof(uint8), fwlen, fp);
/* close the firmware file */
fclose(fp);
if ((int)status < fwlen) {
printf("dhd_download: Short read in %s!\n", fname);
ret = -1;
goto exit;
}
/* Validating the format /length etc of the file */
if ((actual_len = check_file(buf)) <= 0) {
printf("dhd_download: Failed input file check on %s!\n", fname);
ret = -1;
goto exit;
}
/* Read the nvram file into the buffer */
status = fread(buf + actual_len, sizeof(uint8), nvlen, fp1);
/* close the nvram file */
fclose(fp1);
if ((int)status < nvlen) {
printf("dhd_download: Short read in %s!\n", vname);
ret = -1;
goto exit;
}
/* porcess nvram vars if user specifics a text file instead of binary */
nvlen = process_nvram_vars((char*) &buf[actual_len], (unsigned int) nvlen);
if (nvlen % 4) {
pad = 4 - (nvlen % 4);
for (i = 0; i < pad; i ++)
buf[actual_len + nvlen + i] = 0;
nvlen += pad;
}
/* fix up len to be actual len + nvram len */
len = actual_len + nvlen;
/* update trx header with added nvram bytes */
hdr = (struct trx_header *) buf;
hdr->len = htol32(len);
/* pass the actual fw len */
hdr->offsets[TRX_OFFSETS_NVM_LEN_IDX] = htol32(nvlen);
/* caculate CRC over header */
hdr->crc32 = hndcrc32((uint8 *) &hdr->flag_version,
sizeof(struct trx_header) - OFFSETOF(struct trx_header, flag_version),
CRC32_INIT_VALUE);
/* Calculate CRC over data */
for (i = sizeof(struct trx_header); i < (unsigned int)len; ++i)
hdr->crc32 = hndcrc32((uint8 *)&buf[i], 1, hdr->crc32);
hdr->crc32 = htol32(hdr->crc32);
*buffer = buf;
return len;
exit:
if (buf)
free(buf);
return ret;
}
static int
check_file(unsigned char *headers)
{
struct trx_header *trx;
int actual_len = -1;
/* Extract trx header */
trx = (struct trx_header *)headers;
if ((ltoh32(trx->magic)) != TRX_MAGIC) {
printf("check_file: Error: trx bad hdr %x!\n", ltoh32(trx->magic));
return -1;
}
if (ltoh32(trx->flag_version) & TRX_UNCOMP_IMAGE) {
actual_len = ltoh32(trx->offsets[TRX_OFFSETS_DLFWLEN_IDX]) +
sizeof(struct trx_header);
return actual_len;
}
return actual_len;
}
#endif /* SERDOWNLOAD */
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_rxfifo_counters(void *wl, cmd_t *cmd, char **argv)
{
char *statsbuf;
wl_rxfifo_cnt_t cnt;
int err;
void *ptr;
int i;
UNUSED_PARAMETER(cmd);
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_RXFIFO_CNT_VERSION) {
printf("\tIncorrect version of rxfifo counters struct: expected %d; got %d\n",
WL_RXFIFO_CNT_VERSION, cnt.version);
return -1;
}
for (i = 0; i < MAX_RX_FIFO; i++) {
printf("RXFIFO[%d]: data: %u\tmgmtctl: %u \n",
i, cnt.rxf_data[i], cnt.rxf_mgmtctl[i]);
}
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);
}
/* 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;
struct maclist *maclist = (struct maclist *) buf;
uint i, max = (WLC_IOCTL_MAXLEN - sizeof(int)) / ETHER_ADDR_LEN;
struct ether_addr *ea;
memset(&stamon_cfg, 0, sizeof(wlc_stamon_sta_config_t));
if (!*++argv) {
maclist->count = htod32(max);
err = wlu_iovar_get(wl, cmd->name, maclist,
WLC_IOCTL_MAXLEN);
if (!err) {
for (i = 0, ea = maclist->ea; i < maclist->count && i < max; i++, ea++)
printf("%s\n", wl_ether_etoa(ea));
}
} else {
if (!stricmp(*argv, "add")) {
stamon_cfg.cmd = STAMON_CFG_CMD_ADD;
if (!*++argv || !wl_ether_atoe(*argv, &stamon_cfg.ea)) {
printf(" ERROR: no valid ether addr provided\n");
err = -1;
}
} else if (!stricmp(*argv, "del")) {
stamon_cfg.cmd = STAMON_CFG_CMD_DEL;
if (!*++argv || !wl_ether_atoe(*argv, &stamon_cfg.ea)) {
printf(" ERROR: no valid ether addr provided\n");
err = -1;
}
} else if (!stricmp(*argv, "enable"))
stamon_cfg.cmd = STAMON_CFG_CMD_ENB;
else if (!stricmp(*argv, "disable"))
stamon_cfg.cmd = STAMON_CFG_CMD_DSB;
else if (!stricmp(*argv, "counters")) {
const char *cmdname = "sta_monitor_cnt";
uint32 cnt = 0;
if ((err = wlu_iovar_getint(wl, cmdname, (int *)&cnt) < 0)) {
return err;
}
printf("stamon cnt=%u\n", cnt);
return err;
} else if (!stricmp(*argv, "reset_cnts"))
stamon_cfg.cmd = STAMON_CFG_CMD_RSTCNT;
else if (!stricmp(*argv, "stats")) {
stamon_info_t *pbuf;
stamon_cfg.cmd = STAMON_CFG_CMD_GET_STATS;
if (!*++argv || !wl_ether_atoe(*argv, &stamon_cfg.ea)) {
printf(" ERROR: no valid ether addr provided\n");
err = -1;
}
memset(buf, 0, WLC_IOCTL_MAXLEN);
if (!err) {
err = wlu_iovar_getbuf(wl, cmd->name, &stamon_cfg,
sizeof(wlc_stamon_sta_config_t), buf, WLC_IOCTL_MAXLEN);
}
pbuf = (stamon_info_t*)buf;
if (!err) {
if (pbuf->count != 0) {
/* Success case: Found the mac in STAMON and
* read the stats
*/
printf(""MACF" : RSSI :%d \n",
ETHER_TO_MACF(pbuf->sta_data[0].ea),
pbuf->sta_data[0].rssi);
} else if (pbuf->count == 0) {
/* Failed case, Didn't Found the Mac
* read the stats
*/
printf("STAMON not monitoring "MACF" \n",
ETHER_TO_MACF(stamon_cfg.ea));
}
}
return err;
} else {
printf("error: unknown operation option%s\n", *argv);
err = -1;
}
if (!err)
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;
}
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) {
memset(&txfail_config, 0, sizeof(txfail_config));
/* get current txfail configuration */
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);
if (txfail_config.version == AIBSS_TXFAIL_CONFIG_VER_1) {
printf("Max ATIM failures before TXFAIL event: %d \r\n",
txfail_config.max_atim_failure);
}
} else {
txfail_config.bcn_timeout = (uint32) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL) {
printf("Incorrect number of arguments\n");
goto error;
}
txfail_config.max_tx_retry = (uint32) strtoul(*argv, NULL, 0);
if (*++(argv) == NULL) {
printf("Incorrect number of arguments\n");
goto error;
}
txfail_config.max_atim_failure = (uint32) strtoul(*argv, NULL, 0);
if (*++(argv)) {
printf("wrong extra arguments\n");
goto error;
}
txfail_config.version = AIBSS_TXFAIL_CONFIG_VER_1;
txfail_config.len = sizeof(txfail_config);
ret = wlu_iovar_set(wl, cmd->name, (void *) &txfail_config,
sizeof(txfail_config));
}
error:
return ret;
}
/* Returns the matching config table entry from the wl_config_iovar_list for the passed config
* iovar. If no matches are found, then returns the default (last) entry from the list
*/
static wl_config_iovar_t *get_config_iovar_entry(const char *iovar_name)
{
int i = 0;
while (wl_config_iovar_list[i].iovar_name) {
if (!stricmp(iovar_name, wl_config_iovar_list[i].iovar_name))
break;
i++;
}
return &(wl_config_iovar_list[i]);
}
/* Print function for config iovar.
*/
static void wl_bcm_config_print(wl_config_iovar_t *cfg_iovar, wl_config_t *cfg)
{
char *status_str = NULL;
int i = 0;
char *autostr = (cfg->config == (uint32) AUTO) ? "auto" : "";
while (cfg_iovar->params[i].name) {
if (cfg_iovar->params[i].value == cfg->status) {
status_str = cfg_iovar->params[i].name;
break;
}
i++;
}
if (status_str) {
printf("%s %d %s\n", status_str, cfg->status, autostr);
} else {
/* No matching entry found in the table. Just print the value received from
the driver
*/
printf("%d %s\n", cfg->status, autostr);
}
}
int
wl_bcm_config(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
int i = 0;
wl_config_iovar_t *config_iovar;
/* Get the config entry corresponding to this iovar */
config_iovar = get_config_iovar_entry((char *)cmd->name);
if (!config_iovar)
return BCME_ERROR;
if (*++argv == NULL) {
/* Get */
wl_config_t *cfg;
void *ptr = NULL;
if ((err = wlu_var_getbuf_sm(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
cfg = (wl_config_t *) ptr;
cfg->config = dtoh32(cfg->config);
cfg->status = dtoh32(cfg->status);
/* Call the iovar's print function */
config_iovar->pfunc(config_iovar, cfg);
} else {
/* Set */
char *param = *argv++;
bool found = 0;
wl_config_t cfg;
i = 0;
/* Check if the passed param exist in the config_iovar table */
while (config_iovar->params[i].name) {
if (!stricmp(config_iovar->params[i].name, param)) {
cfg.config = config_iovar->params[i].value;
found = 1;
break;
}
i++;
}
if (!found) {
/* Check if an integer value is passed as the param */
char *endptr = NULL;
cfg.config = (uint32) strtol(param, &endptr, 0);
if (*endptr == '\0')
found = 1;
}
if (!found) {
printf("Unsupported parameter [%s]\n", param);
return -1;
}
cfg.config = htod32(cfg.config);
err = wlu_var_setbuf(wl, cmd->name, &cfg, sizeof(wl_config_t));
}
return err;
}
/* Set or Get the "desired_bssid" ioctl
*/
static int
wl_desired_bssid(void *wl, cmd_t *cmd, char **argv)
{
struct ether_addr ea;
int error = BCME_OK;
UNUSED_PARAMETER(cmd);
argv++;
if (*argv == NULL) {
if ((error = wlu_get(wl, WLC_GET_DESIRED_BSSID, &ea, ETHER_ADDR_LEN)) < 0) {
return error;
}
printf("%s\n", wl_ether_etoa(&ea));
} else {
if (!wl_ether_atoe(*argv, &ea))
return BCME_USAGE_ERROR;
error = wlu_set(wl, WLC_SET_DESIRED_BSSID, &ea, ETHER_ADDR_LEN);
}
return error;
}
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
static int wl_dump_modesw_dyn_bwsw(void *wl, cmd_t *cmd, char **argv)
{
char *ptr;
int err = 0;
if (*++argv != NULL) {
return BCME_UNSUPPORTED;
}
if ((err = wlu_iovar_getbuf(wl, cmd->name, NULL, 0,
buf, WLC_IOCTL_MAXLEN)) < 0) {
return err;
}
ptr = (char *) buf;
fputs(ptr, stdout);
return err;
}
#endif
static int
wl_dfs_channel_forced(void *wl, cmd_t *cmd, char **argv)
{
uint i;
int err = -1;
char *p;
chanspec_t chanspec;
wl_dfs_forced_t *dfs_frcd;
wl_dfs_forced_t inp;
uint32 ioctl_size;
dfs_frcd = (wl_dfs_forced_t *)buf;
memset(buf, 0, WL_DFS_FORCED_PARAMS_MAX_SIZE);
memset(&inp, 0, sizeof(wl_dfs_forced_t));
/* Get the existing configuration first. We need this for
* set operations also
*/
inp.version = DFS_PREFCHANLIST_VER;
if ((err = wlu_iovar_getbuf(wl, cmd->name, &inp, sizeof(wl_dfs_forced_t),
dfs_frcd, WL_DFS_FORCED_PARAMS_MAX_SIZE)) < 0) {
return err;
}
if (!argv[1]) {
/* Get Operation */
char chanbuf[CHANSPEC_STR_LEN];
/* List configuration shouldn't be there if we are operating on
* old version of driver
*/
if ((dfs_frcd->version == DFS_PREFCHANLIST_VER) && (dfs_frcd->chspec_list.num)) {
printf("DFS Preferred channel list:: \n");
for (i = 0; i < dfs_frcd->chspec_list.num; i++) {
chanspec =
wl_chspec32_from_driver(dfs_frcd->chspec_list.list[i]);
/* wf_chspec_ntoa will return NULL when N mode is disabled */
if (wf_chspec_ntoa(chanspec, chanbuf))
printf("%s(0x%x) ", chanbuf, chanspec);
else
printf("(0x%x) ", chanspec);
}
printf("\n");
} else {
if (dfs_frcd->version == DFS_PREFCHANLIST_VER) {
/* Not configured since new driver works on list */
printf("DFS Preferred Channel:: 0x0 (None)\n");
} else {
chanspec = wl_chspec32_from_driver(dfs_frcd->chspec);
/* wf_chspec_ntoa will return NULL when N mode is disabled */
if (chanspec && wf_chspec_ntoa(chanspec, chanbuf)) {
printf("DFS Preferred Channel:: %s (0x%x)\n",
chanbuf, chanspec);
} else {
printf("DFS Preferred Channel:: 0x%x\n", chanspec);
}
}
}
return err;
}
if (!strcmp(argv[1], "-l")) {
/* List configuration */
if (!argv[2]) {
printf("Please provide channel list\n");
err = BCME_USAGE_ERROR;
return err;
}
if (dfs_frcd->version != DFS_PREFCHANLIST_VER) {
printf("List Configuration is not supported in this version of driver\n");
return err;
}
p = strtok(argv[2], ", ");
while (p) {
if ((*p != '+') && (*p != '-')) {
printf("channel should be prefixed with +/-\n");
err = BCME_USAGE_ERROR;
return err;
}
if (!(chanspec = wf_chspec_aton(p + 1))) {
printf("Invalid channel specified\n");
err = BCME_USAGE_ERROR;
return err;
}
if (!CHSPEC_IS5G(chanspec)) {
printf("Invalid channel specified\n");
err = BCME_USAGE_ERROR;
return err;
}
dfs_frcd->chspec = 0;
if (*p == '+') {
/* check if exists */
for (i = 0; i < dfs_frcd->chspec_list.num; i++) {
if (chanspec == dfs_frcd->chspec_list.list[i]) {
printf("Ignoring chanspec 0x%x\n", chanspec);
goto next_token;
}
}
chanspec = wl_chspec32_to_driver(chanspec);
dfs_frcd->chspec_list.list[dfs_frcd->chspec_list.num++] = chanspec;
} else if (*p == '-') {
/* check if exists */
for (i = 0; i < dfs_frcd->chspec_list.num; i++) {
if (chanspec == dfs_frcd->chspec_list.list[i]) {
dfs_frcd->chspec_list.num--;
break;
}
}
while (i < dfs_frcd->chspec_list.num) {
dfs_frcd->chspec_list.list[i] =
dfs_frcd->chspec_list.list[i+1];
i++;
}
}
next_token:
p = strtok(NULL, ", ");
}
if (dfs_frcd->chspec_list.num > WL_NUMCHANNELS) {
printf("Maximum %d channels supported\n", WL_NUMCHANNELS);
err = BCME_USAGE_ERROR;
return err;
}
ioctl_size = WL_DFS_FORCED_PARAMS_FIXED_SIZE +
(dfs_frcd->chspec_list.num * sizeof(chanspec_t));
dfs_frcd->version = DFS_PREFCHANLIST_VER;
err = wlu_iovar_set(wl, cmd->name, dfs_frcd, ioctl_size);
} else {
/* No list provided. Either single channel or clear list */
if (dfs_frcd->version == DFS_PREFCHANLIST_VER) {
/* Clear configuration */
dfs_frcd->chspec = 0;
dfs_frcd->chspec_list.num = 0;
ioctl_size = WL_DFS_FORCED_PARAMS_FIXED_SIZE +
(dfs_frcd->chspec_list.num * sizeof(chanspec_t));
err = wlu_iovar_set(wl, cmd->name, dfs_frcd, ioctl_size);
}
/* Single channel configuration. Continue as we were doing earlier */
if (strcmp(argv[1], "0"))
err = wl_chanspec(wl, cmd, argv);
}
return err;
}
#ifdef WIN32
#pragma warning(pop)
#endif
static int
wl_setiproute(void *wl, cmd_t *cmd, char **argv)
{
uint route_tbl_len;
wlc_ipfo_route_tbl_t *route_tbl = NULL;
uint32 entries;
char *endptr;
uint32 i = 0;
struct ipv4_addr dipaddr;
struct ether_addr ea;
int argc;
int ret = BCME_OK;
int buflen = sprintf(buf, "%s", *argv) + 1;
UNUSED_PARAMETER(cmd);
argv++;
route_tbl_len = WL_IPFO_ROUTE_TBL_FIXED_LEN +
WL_MAX_IPFO_ROUTE_TBL_ENTRY * sizeof(wlc_ipfo_route_entry_t);
/* allocate the max storage */
if ((route_tbl = malloc(route_tbl_len)) == NULL) {
fprintf(stderr, "Error allocating %d bytes for route table\n", route_tbl_len);
return BCME_NOMEM;
}
memset(route_tbl, 0, route_tbl_len);
if (*argv == NULL) {
if ((ret = wlu_iovar_get(wl, buf, route_tbl, route_tbl_len)) == BCME_OK) {
if (route_tbl->num_entry == 0) {
printf("No entries present\n");
} else {
for (i = 0; i < route_tbl->num_entry; i++) {
printf("entry%d", i);
printf("\t%s",
wl_iptoa(&route_tbl->route_entry[i].ip_addr));
printf("\t%s\n",
wl_ether_etoa(&route_tbl->route_entry[i].nexthop));
}
}
}
} else {
argc = ARGCNT(argv);
if (argc <= 0)
goto usage;
entries = strtoul(argv[0], &endptr, 0);
if (*endptr != '\0')
goto usage;
if ((uint32)argc != (entries * 2 + 1))
goto usage;
route_tbl->num_entry = entries;
argv++;
for (i = 0; i < entries; i++) {
if (!wl_atoip(argv[i*2], &dipaddr))
goto usage;
if (!wl_ether_atoe(argv[(i * 2 + 1)], &ea))
goto usage;
memcpy(&route_tbl->route_entry[i].ip_addr, &dipaddr, IPV4_ADDR_LEN);
memcpy(&route_tbl->route_entry[i].nexthop, &ea, ETHER_ADDR_LEN);
}
route_tbl_len = (entries * sizeof(wlc_ipfo_route_entry_t)) + IPV4_ADDR_LEN;
memcpy(&buf[buflen], route_tbl, route_tbl_len);
ret = wlu_set(wl, WLC_SET_VAR, &buf[0], buflen + route_tbl_len);
}
free(route_tbl);
return ret;
usage:
fprintf(stderr, "wrong command format\n");
if (route_tbl != NULL)
free(route_tbl);
return ret;
}
static int
wl_modesw_timecal(void *wl, cmd_t *cmd, char **argv)
{
int val;
int err = 0;
char *ptr = NULL;
if (*++argv == NULL) {
/* retrieving the results */
if ((err = wlu_iovar_getbuf(wl, cmd->name, NULL, 0,
buf, WLC_IOCTL_MAXLEN)) < 0) {
return err;
}
ptr = buf;
fputs(ptr, stdout);
}
else
{
val = htod32(atoi(*argv));
if ((err = wlu_iovar_setbuf(wl, cmd->name, &val, sizeof(val),
buf, WLC_IOCTL_MAXLEN)) < 0) {
return err;
}
}
return err;
}
static int
wl_pcie_bus_throughput_params(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, opt_err;
miniopt_t to;
pcie_bus_tput_params_t *params = NULL;
pcie_bus_tput_stats_t *stats = NULL;
UNUSED_PARAMETER(cmd);
argv++; /* toss the command name */
if (!*argv) { /* Get the pcie bus throughput stats */
void *ptr = NULL;
uint32 tput = 0;
if ((err = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0) {
fprintf(stderr, "Failed to get stats.\n");
return err;
}
stats = (pcie_bus_tput_stats_t *)ptr;
if (stats->count) {
tput = (stats->count * stats->nbytes_per_descriptor) /
stats->time_taken;
tput = (tput * 8) / (1024 * 1024); /* convert to Mega bits */
fprintf(stdout, "Seconds test run %d\nNo of dma completed %d\n"
"Bytes transfered per dma %d\nBus throughput: %d mbps\n",
stats->time_taken, stats->count,
stats->nbytes_per_descriptor, tput);
}
}
if (*argv) { /* Set the bus throughput params */
params = (pcie_bus_tput_params_t *)malloc(sizeof(pcie_bus_tput_params_t));
if (params == NULL) {
fprintf(stderr, "Failed to allocate buffer.\n");
return BCME_NOMEM;
}
memset(params, 0, sizeof(*params));
miniopt_init(&to, __FUNCTION__, NULL, FALSE);
while ((opt_err = miniopt(&to, argv)) != -1) {
if (opt_err == 1) {
err = BCME_USAGE_ERROR;
goto fail;
}
argv += to.consumed;
if (to.opt == 'n') {
if (!to.good_int) {
fprintf(stderr, "%s: could not parse \"%s\" as an int for"
" max_dma_descriptors \n", __FUNCTION__, to.valstr);
err = BCME_BADARG;
goto fail;
}
params->max_dma_descriptors = to.val;
}
}
if (params->max_dma_descriptors == 0)
params->max_dma_descriptors = 64; /* set default as 64 */
if ((err = wlu_var_setbuf(wl, cmd->name, params,
sizeof(*params)) < 0)) {
fprintf(stderr, "failed to trigger measurement %d\n", err);
}
}
fail:
if (params)
free(params);
return err;
}
static int
wl_interface_create_action(void *wl, cmd_t *cmd, char **argv)
{
wl_interface_create_t wlif;
wl_interface_info_t *pwlif_info;
int count, val;
int err;
char opt, *p, *valstr, *endptr = NULL;
memset(&wlif, 0, sizeof(wlif));
wlif.ver = WL_INTERFACE_CREATE_VER;
argv++;
count = ARGCNT(argv);
/*
* We should have atleast one argument for the create command,
* whether to start it as AP or STA.
*/
if (count < 1)
return BCME_USAGE_ERROR;
/*
* First Argument:
* Find the interface that user need to create and update the flag/iftype
* flags field is still used along with iftype inorder to support the old version of the
* FW work with the latest app changes.
*/
if (stricmp(argv[0], "sta") == 0) {
wlif.iftype = WL_INTERFACE_TYPE_STA;
wlif.flags |= WL_INTERFACE_CREATE_STA;
} else if (stricmp(argv[0], "ap") == 0) {
wlif.iftype = WL_INTERFACE_TYPE_AP;
wlif.flags |= WL_INTERFACE_CREATE_AP;
} else {
return BCME_USAGE_ERROR;
}
argv++;
while ((p = *argv) != NULL) {
argv++;
opt = '\0';
valstr = NULL;
if (!strncmp(p, "-", 1)) {
opt = p[1];
if (strlen(p) > 2) {
fprintf(stderr,
"%s: only single char options, error on param \"%s\"\n",
__FUNCTION__, p);
err = BCME_BADARG;
goto exit;
}
if (*argv == NULL) {
fprintf(stderr,
"%s: missing value parameter after \"%s\"\n",
__FUNCTION__, p);
err = BCME_USAGE_ERROR;
goto exit;
}
valstr = *argv;
argv++;
} else {
err = BCME_USAGE_ERROR;
goto exit;
}
/* The mac address is optional, if its passed and valid use it. */
if (opt == 'm') {
if (wl_ether_atoe(valstr, &wlif.mac_addr)) {
wlif.flags |= WL_INTERFACE_MAC_USE;
}
}
/* The wlc_index is optional, if its passed and valid use it. */
if (opt == 'c') {
/* parse valstr as int */
val = (int)strtol(valstr, &endptr, 0);
if (*endptr == '\0') {
wlif.flags |= WL_INTERFACE_WLC_INDEX_USE;
wlif.wlc_index = val;
} else {
fprintf(stderr,
"wl_interface_create_action: could not parse \"%s\" as an int",
valstr);
err = BCME_BADARG;
goto exit;
}
}
}
err = wlu_var_getbuf(wl, cmd->name, &wlif, sizeof(wlif), (void *)&pwlif_info);
if (err < 0) {
printf("%s(): wlu_var_getbuf failed %d \r\n", __FUNCTION__, err);
} else {
printf("ifname: %s bsscfgidx: %d mac_addr %s\r\n",
pwlif_info->ifname, pwlif_info->bsscfgidx,
wl_ether_etoa(&pwlif_info->mac_addr));
}
exit:
return err;
}
static int
wl_interface_remove_action(void *wl, cmd_t *cmd, char **argv)
{
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, "interface_remove", &bsscfg_idx, &consumed)) != 0)
return error;
argv += consumed;
/*
* This command supports both "bss" method and "-i" method. First
* check if "bss" options is present, and if yes, use the index
* otherwise use the normal path.
*/
if (consumed != 0)
error = wl_bssiovar_set(wl, "interface_remove", bsscfg_idx, NULL, 0);
else
error = wlu_var_setbuf(wl, cmd->name, NULL, 0);
return error;
}
static int
wl_phy_txpwrcap_tbl(void *wl, cmd_t *cmd, char **argv)
{
wl_txpwrcap_tbl_t txpwrcap_tbl;
wl_txpwrcap_tbl_t *txpwrcap_tbl_ptr;
uint8 num_antennas = 0;
void *ptr = NULL;
int err = 0;
uint8 i, j, k;
if (!(*++argv)) { /* Get */
if ((err = wlu_var_getbuf_med(wl, cmd->name, NULL, 0, &ptr)) < 0)
return err;
txpwrcap_tbl_ptr = ptr;
for (i = 0; i < TXPWRCAP_MAX_NUM_CORES; i++) {
if (txpwrcap_tbl_ptr->num_antennas_per_core[i]) {
printf("Num Antennas on Core %d = %d\n",
i, txpwrcap_tbl_ptr->num_antennas_per_core[i]);
num_antennas += txpwrcap_tbl_ptr->num_antennas_per_core[i];
}
}
printf("Pwr Caps with Cell On:\n");
for (i = 0; i < num_antennas; i++) {
printf("\t%d", txpwrcap_tbl_ptr->pwrcap_cell_on[i]);
}
printf("\n");
printf("Pwr Caps with Cell Off:\n");
for (i = 0; i < num_antennas; i++) {
printf("\t%d", txpwrcap_tbl_ptr->pwrcap_cell_off[i]);
}
printf("\n");
return err;
} else { /* Set */
txpwrcap_tbl.num_antennas_per_core[0] = strtol(*argv, NULL, 0);
num_antennas = txpwrcap_tbl.num_antennas_per_core[0];
for (i = 1; i < TXPWRCAP_MAX_NUM_CORES; i++) {
if (*++argv) {
txpwrcap_tbl.num_antennas_per_core[i] = strtol(*argv, NULL, 0);
num_antennas += txpwrcap_tbl.num_antennas_per_core[i];
} else {
return BCME_USAGE_ERROR;
}
}
if (*++argv) {
memset(txpwrcap_tbl.pwrcap_cell_on, 127,
TXPWRCAP_MAX_NUM_ANTENNAS *
sizeof(txpwrcap_tbl.pwrcap_cell_on[0]));
memset(txpwrcap_tbl.pwrcap_cell_off, 127,
TXPWRCAP_MAX_NUM_ANTENNAS *
sizeof(txpwrcap_tbl.pwrcap_cell_off[0]));
i = 0;
j = 0;
k = 0;
do {
if (k >= 2 * num_antennas) {
printf("Entries exceeded max allowed\n");
return BCME_ERROR;
}
if (k >= num_antennas)
txpwrcap_tbl.pwrcap_cell_off[j++] = strtoul(*argv, NULL, 0);
else
txpwrcap_tbl.pwrcap_cell_on[i++] = strtoul(*argv, NULL, 0);
k++;
} while (*++argv);
if ((k != 2* num_antennas) &&
(k != num_antennas)) {
printf("Incorrect Number of Entries. Expected %d/%d, Entered %d\n",
num_antennas, 2 * num_antennas, k);
return BCME_ERROR;
}
if ((err = wlu_var_setbuf(wl, cmd->name, &txpwrcap_tbl,
sizeof(txpwrcap_tbl))) < 0) {
printf("Unable to set the txpwrcaps.\n");
printf("Check number of antennas for this board.\n");
return BCME_ERROR;
}
}
else
return BCME_USAGE_ERROR;
}
return err;
}
/*
* Get Beacon Trim Stats
* wl bcntrim_stats
*/
static int
wl_bcntrim_stats(void *wl, cmd_t *cmd, char **argv)
{
int err;
uint16 op_buffer[BCNTRIMST_NUM];
UNUSED_PARAMETER(cmd);
argv++;
if (*argv == NULL) {
if ((err = wlu_iovar_get(wl, cmd->name, (void *) op_buffer,
(BCNTRIMST_NUM * 2))) < 0)
return (err);
printf("- Beacon Trim Statistics -\n");
printf("BCNTRIM_PER : %d\n", op_buffer[BCNTRIMST_PER]);
printf("BCNTRIM_TIMEND : %d\n", op_buffer[BCNTRIMST_TIMEND]);
printf("BCNTRIM_TSFLMT : %d\n", op_buffer[BCNTRIMST_TSFLMT]);
printf("BCNTRIM_CUR : %d\n", op_buffer[BCNTRIMST_CUR]);
printf("BCNTRIM_PREVLEN : %d\n", op_buffer[BCNTRIMST_PREVLEN]);
printf("BCNTRIM_TIMLEN : %d\n", op_buffer[BCNTRIMST_TIMLEN]);
printf("BCNTRIM_RSSI : %d\n", op_buffer[BCNTRIMST_RSSI]);
printf("BCNTRIM_CHAN : %d\n", op_buffer[BCNTRIMST_CHAN]);
printf("BCNTRIM_DUR : %d\n", op_buffer[BCNTRIMST_DUR]);
printf("BCNTRIM_RXMBSS : %d\n", op_buffer[BCNTRIMST_RXMBSS]);
printf("BCNTRIM_CANTRIM : %d\n", op_buffer[BCNTRIMST_CANTRIM]);
printf("BCNTRIM_LENCHG : %d\n", op_buffer[BCNTRIMST_LENCHG]);
printf("BCNTRIM_TSFDRF : %d\n", op_buffer[BCNTRIMST_TSFDRF]);
printf("BCNTRIM_NOTIM : %d\n", op_buffer[BCNTRIMST_NOTIM]);
} else {
/* Set not supported */
return USAGE_ERROR;
}
return err;
}
#ifdef ATE_BUILD
static int
wl_gpaio(void *wl, cmd_t *cmd, char **argv)
{
char **p = argv;
int counter = 0;
wl_gpaio_option_t option;
BCM_REFERENCE(cmd);
while (*p) {
counter++;
p++;
}
if (counter != 2) {
return USAGE_ERROR;
}
if (strcmp("pmu_afeldo", argv[1]) == 0) {
option = GPAIO_PMU_AFELDO;
} else if (strcmp("pmu_txldo", argv[1]) == 0) {
option = GPAIO_PMU_TXLDO;
} else if (strcmp("pmu_vcoldo", argv[1]) == 0) {
option = GPAIO_PMU_VCOLDO;
} else if (strcmp("pmu_lnaldo", argv[1]) == 0) {
option = GPAIO_PMU_LNALDO;
} else if (strcmp("pmu_adcldo", argv[1]) == 0) {
option = GPAIO_PMU_ADCLDO;
} else if (strcmp("clear", argv[1]) == 0) {
option = GPAIO_PMU_CLEAR;
} else {
return USAGE_ERROR;
}
return (wlu_iovar_setint(wl, argv[0], (int)option));
}
#endif /* ATE_BUILD */
static int
wl_macdbg_pmac(void *wl, cmd_t *cmd, char **argv)
{
wl_macdbg_pmac_param_t pmac;
int err = BCME_OK;
char *p, opt;
char *retbuf;
memset(&pmac, 0, sizeof(pmac));
retbuf = malloc(WL_DUMP_BUF_LEN);
if (retbuf == NULL) {
printf("No memory to allocate return buffer\n");
return BCME_NOMEM;
}
/* skip the command name */
argv++;
/* Get the selection */
if ((p = *argv) == NULL) {
err = BCME_USAGE_ERROR;
goto exit;
}
strncpy(pmac.type, p, MIN(strlen(p), sizeof(pmac.type)));
pmac.type[sizeof(pmac.type) - 1] = '\0';
/* skip the type */
argv++;
pmac.step = (uint8)(-1);
pmac.num = 0;
pmac.bitmap = 0;
pmac.addr_raw = FALSE;
pmac.w_en = FALSE;
while ((p = *argv)) {
argv++;
if (!strncmp(p, "-", 1)) {
if (strlen(p) > 2 || (p[1] != 'r' && *argv == NULL)) {
err = BCME_USAGE_ERROR;
goto exit;
}
opt = p[1];
switch (opt) {
case 's':
pmac.step = strtol(*argv, NULL, 0);
argv++;
break;
case 'n':
pmac.num = strtol(*argv, NULL, 0);
argv++;
break;
case 'b':
pmac.bitmap = strtoul(*argv, NULL, 0);
argv++;
break;
case 'r':
pmac.addr_raw = TRUE;
break;
case 'w':
pmac.w_val = strtoul(*argv, NULL, 0);
pmac.w_en = TRUE;
argv++;
break;
default:
printf("Invalid option!!\n");
err = BCME_USAGE_ERROR;
goto exit;
}
} else {
pmac.addr[pmac.addr_num++] = strtol(p, NULL, 0);
if (pmac.addr_num >= MACDBG_PMAC_ADDR_INPUT_MAXNUM) {
printf("Reached input limitation!!\n");
err = BCME_USAGE_ERROR;
goto exit;
}
}
}
if ((err = wlu_iovar_getbuf(wl, cmd->name, &pmac,
sizeof(pmac), retbuf, WL_DUMP_BUF_LEN) < 0)) {
goto exit;
}
if (!pmac.w_en) {
fputs(retbuf, stdout);
}
exit:
if (retbuf) {
free(retbuf);
}
return err;
}
static int wl_mu_rate(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
uint32 i = 0;
char *endptr = NULL;
mu_rate_t mu;
BCM_REFERENCE(cmd);
memset(&mu, 0x0, sizeof(mu));
if (!argv[1]) {
if ((err = wlu_iovar_getbuf(wl, cmd->name, NULL,
0, &mu, sizeof(mu))) < 0) {
fprintf(stderr, "Error reading svmp memory %s %d\n", argv[0], err);
goto exit;
}
for (i = 0; i < 4; i++) {
printf("0x%04x ", mu.rate_user[i]);
}
printf("%s\n", mu.auto_rate ? "(Auto)" : "(Fixed)");
return err;
}
/* auto rate */
if (!stricmp(argv[1], "auto") || (!stricmp(argv[1], "-1"))) {
/* turn on auto rate */
/* wl svmp_mem 0x20060 1 0 */
mu.auto_rate = 1;
err = wlu_var_setbuf(wl, cmd->name, &mu, sizeof(mu));
} else {
for (i = 0; i < 4; i++) {
mu.rate_user[i] = 0xffff;
}
/* set rates */
mu.auto_rate = 0;
for (i = 1; i < 5; i++) {
if (!argv[i])
break;
mu.rate_user[i-1] = strtol(argv[i], &endptr, 0);
}
err = wlu_var_setbuf(wl, cmd->name, &mu, sizeof(mu));
/* barf if set mu_rate but blocked by mu_group */
if (err != BCME_OK) {
printf("Set fix rate failed!!!\n");
printf("Check if blocked by mu_group setting or other error\n");
}
}
exit:
return err;
}
static int wl_mu_group(void *wl, cmd_t *cmd, char **argv)
{
int m, n, ret = 0;
int16 temp;
mu_group_t mu_group;
mu_group.version = WL_MU_GROUP_PARAMS_VERSION;
mu_group.forced = WL_MU_GROUP_ENTRY_EMPTY;
mu_group.forced_group_mcs = WL_MU_GROUP_ENTRY_EMPTY;
mu_group.forced_group_num = 0;
mu_group.auto_group_num = 0;
mu_group.group_method = WL_MU_GROUP_ENTRY_EMPTY;
mu_group.group_number = WL_MU_GROUP_ENTRY_EMPTY;
for (m = 0; m < WL_MU_GROUP_NGROUP_MAX; m++) {
for (n = 0; n < WL_MU_GROUP_NUSER_MAX; n++) {
mu_group.group_option[m][n] = WL_MU_GROUP_ENTRY_EMPTY;
}
mu_group.group_GID[m] = WL_MU_GROUP_ENTRY_EMPTY;
}
if (!argv[1]) {
/* read mode */
ret = wlu_iovar_getbuf(wl, cmd->name, NULL, 0, &mu_group, sizeof(mu_group));
if (mu_group.version != WL_MU_GROUP_PARAMS_VERSION) {
printf("\tIncorrect version "
"of mu_group struct: expect %d but get %d\n",
WL_MU_GROUP_PARAMS_VERSION, mu_group.version);
return BCME_BADARG;
}
printf("mu_group: ");
if (mu_group.forced == WL_MU_GROUP_MODE_FORCED) {
if (mu_group.forced_group_mcs == WL_MU_GROUP_MODE_FORCED) {
printf("forced (fixed MCS)\n");
} else {
printf("forced (auto MCS)\n");
}
for (m = 0; m < mu_group.forced_group_num; m++) {
printf(" Group %d: ", m);
for (n = 0; n < WL_MU_GROUP_NUSER_MAX; n++) {
if (mu_group.group_option[m][n]
!= WL_MU_GROUP_ENTRY_EMPTY) {
printf("0x%03x ",
mu_group.group_option[m][n]);
}
}
printf("\n");
}
} else {
printf("auto\n");
printf("VASIP grouping method: ");
if (mu_group.group_method > 0) {
printf("new method %d (%s)\n",
mu_group.group_method, mu_group.group_method_name);
} else {
printf("old method ");
printf("(one group for all admitted users with GID=9)\n");
}
printf(" group number: %d\n", mu_group.group_number);
if (mu_group.auto_group_num > 0) {
printf("Latest recommended groups:\n");
}
for (m = 0; m < mu_group.auto_group_num; m++) {
printf(" Group %d: ", m);
for (n = 0; n < WL_MU_GROUP_NUSER_MAX; n++) {
if (mu_group.group_option[m][n]
!= WL_MU_GROUP_ENTRY_EMPTY) {
printf("0x%03x ",
mu_group.group_option[m][n]);
} else {
printf(" --- ");
}
}
printf("(GID=%d)\n", mu_group.group_GID[m]);
}
}
} else {
argv++;
do {
char *s = *argv++;
if (!strcmp(s, "-h")) {
ret = BCME_USAGE_ERROR;
goto exit_mu_group;
} else if (!strcmp(s, "-m")) {
mu_group.group_method = (int16)strtol(*argv++, NULL, 0);
if (mu_group.group_method < WL_MU_GROUP_METHOD_MIN) {
ret = BCME_USAGE_ERROR;
printf("Incorrect -m: M<%d\n", WL_MU_GROUP_METHOD_MIN);
goto exit_mu_group;
}
} else if (!strcmp(s, "-n")) {
mu_group.group_number = (int16)strtol(*argv++, NULL, 0);
if ((mu_group.group_number < WL_MU_GROUP_NUMBER_AUTO_MIN) ||
(mu_group.group_number >
WL_MU_GROUP_NUMBER_AUTO_MAX)) {
ret = BCME_USAGE_ERROR;
printf("Incorrect '-n': N is not in the range %d~%d\n",
WL_MU_GROUP_NUMBER_AUTO_MIN,
WL_MU_GROUP_NUMBER_AUTO_MAX);
goto exit_mu_group;
}
} else if (!strcmp(s, "-f")) {
temp = (int)strtol(*argv++, NULL, 0);
if ((temp == WL_MU_GROUP_MODE_AUTO) ||
(temp == WL_MU_GROUP_MODE_FORCED)) {
mu_group.forced_group_mcs = temp;
}
} else if ((!strcmp(s, "-g")) && (mu_group.forced != 0)) {
temp = (int)strtol(*argv++, NULL, 0);
if (temp == WL_MU_GROUP_AUTO_COMMAND) {
mu_group.forced = WL_MU_GROUP_MODE_AUTO;
mu_group.forced_group_num = 0;
} else if ((temp < WL_MU_GROUP_NUMBER_FORCED_MAX) &&
(temp == mu_group.forced_group_num)) {
mu_group.forced = WL_MU_GROUP_MODE_FORCED;
mu_group.forced_group_num += 1;
m = temp;
n = 0;
} else {
ret = BCME_USAGE_ERROR;
printf("Incorrect to set froced group options: ");
if (temp != mu_group.forced_group_num) {
printf("group index should be successive\n");
}
if (mu_group.forced_group_num >=
WL_MU_GROUP_NUMBER_FORCED_MAX) {
printf("support up to %d forced options\n",
WL_MU_GROUP_NUMBER_FORCED_MAX);
}
goto exit_mu_group;
}
} else if (((mu_group.forced_group_num > 0)) && (n < 4)) {
mu_group.group_option[m][n] = (int16)strtol(s, NULL, 0);
n += 1;
}
} while (*argv);
if ((mu_group.forced == WL_MU_GROUP_MODE_FORCED) &&
((mu_group.group_method != WL_MU_GROUP_ENTRY_EMPTY) ||
(mu_group.group_number != WL_MU_GROUP_ENTRY_EMPTY))) {
ret = BCME_USAGE_ERROR;
printf("Incorrect to set forced grouping options "
"with auto grouping parameters\n");
goto exit_mu_group;
}
/* default is forced mcs for forced grouping */
/* if not specified forced_group_mcs when forced grouping, set forced_group_mcs=1 */
if ((mu_group.forced == WL_MU_GROUP_MODE_FORCED) &&
(mu_group.forced_group_mcs == WL_MU_GROUP_ENTRY_EMPTY)) {
mu_group.forced_group_mcs = WL_MU_GROUP_MODE_FORCED;
}
/* set mode */
ret = wlu_var_setbuf(wl, cmd->name, &mu_group, sizeof(mu_group));
}
exit_mu_group:
return ret;
}
static int wl_mu_policy(void *wl, cmd_t *cmd, char **argv)
{
mu_policy_t policy;
int err = -1;
uint32 val;
memset(&policy, 0, sizeof(mu_policy_t));
policy.version = WL_MU_POLICY_PARAMS_VERSION;
policy.length = sizeof(mu_policy_t);
if ((err = wlu_iovar_getbuf(wl, cmd->name, NULL, 0, &policy, sizeof(mu_policy_t))) < 0) {
return err;
}
/* Check the iovar version */
if (policy.version != WL_MU_POLICY_PARAMS_VERSION) {
err = BCME_BADARG;
goto exit_mu_policy;
}
if (!argv[1]) {
/* read mode */
printf("Current MU policy settings:\n");
printf(" scheduler: %s", policy.sched_timer? "ON":"OFF");
if (policy.sched_timer)
printf(", timer: %u seconds\n", policy.sched_timer);
else
printf(" \n");
printf(" performance monitors: %s\n", policy.pfmon? "ON":"OFF");
printf(" gpos performance monitors: %s\n", policy.pfmon_gpos? "ON":"OFF");
printf(" forced the same BW check: %s\n", policy.samebw? "ON":"OFF");
printf(" max number of rx streams in the clients: %u\n", policy.nrx);
printf(" max number of admitted clients: %u\n", policy.max_muclients);
} else {
argv++;
do {
char *s = *argv++;
if (!strcmp(s, "-h")) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
} else if (!strcmp(s, "-sched_timer")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
policy.sched_timer = val;
} else if (!strcmp(s, "-pfmon")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
if ((val == WL_MU_POLICY_DISABLED) ||
(val == WL_MU_POLICY_ENABLED)) {
policy.pfmon = val;
} else {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
} else if (!strcmp(s, "-pfmon_gpos")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
if ((val == WL_MU_POLICY_DISABLED) ||
(val == WL_MU_POLICY_ENABLED)) {
policy.pfmon_gpos = val;
} else {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
} else if (!strcmp(s, "-samebw")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
if ((val == WL_MU_POLICY_DISABLED) ||
(val == WL_MU_POLICY_ENABLED)) {
policy.samebw = val;
} else {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
} else if (!strcmp(s, "-nrx")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
if ((val >= WL_MU_POLICY_NRX_MIN) &&
(val <= WL_MU_POLICY_NRX_MAX)) {
policy.nrx = val;
} else {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
} else if (!strcmp(s, "-max_muclients")) {
if (!*argv) {
err = BCME_USAGE_ERROR;
goto exit_mu_policy;
}
val = strtoul(*argv++, NULL, 0);
policy.max_muclients = val;
}
} while (*argv);
/* set mode */
err = wlu_var_setbuf(wl, cmd->name, &policy, sizeof(policy));
}
exit_mu_policy:
return err;
}
static int wl_svmp_mem(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
svmp_mem_t mem;
uint16 *svmp_buf;
uint32 i;
char *endptr = NULL;
if (!argv[1]) {
fprintf(stderr, "Too few arguments\n");
return BCME_USAGE_ERROR;
}
mem.addr = strtol(argv[1], &endptr, 0);
if (!argv[2]) {
fprintf(stderr, "%s: Length must be specified\n", cmd->name);
err = BCME_USAGE_ERROR;
goto exit;
}
mem.len = strtol(argv[2], &endptr, 0);
if (argv[3]) {
mem.val = strtol(argv[3], &endptr, 0);
err = wlu_var_setbuf(wl, cmd->name, &mem, sizeof(mem));
goto exit;
}
if ((err = wlu_iovar_getbuf(wl, cmd->name, &mem,
sizeof(mem), buf, WLC_IOCTL_MAXLEN)) < 0) {
fprintf(stderr, "Error reading svmp memory %s %d\n", argv[0], err);
goto exit;
}
svmp_buf = (uint16 *)buf;
for (i = 0; i < mem.len; i++) {
if (!(i&1) && (i+1 < mem.len))
printf("0x%04x", svmp_buf[i+1]);
else {
if ((i%2 != 0) || (i+1 < mem.len))
printf("%04x", svmp_buf[i-1]);
else
printf("0x%04x", svmp_buf[i]);
}
if (i && (i%2))
printf("\n");
}
printf("\n");
exit:
return err;
}
#if defined(BCMDBG) || defined(BCMDBG_DUMP)
static void
wl_svmp_sampcol_printf_configs(wl_svmp_sampcol_t *psampcol, uint16 mode)
{
char *str_phy1mux[] = {"gpioOut", "fftOut", "dbgHx", "rx1mux"};
char *str_rx1mux[] = {"farrowOut", "iqCompOut", "dcFilterOut",
"rxFilterOut", "aciFilterOut"};
char *str_packmode[] = {"dual", "4-cores", "2-cores", "single-core"};
char *str_packorder[] = {"big", "little"};
printf("version of svmp sample collect params: %d\n", psampcol->version);
printf(" enable: %d\n", psampcol->enable);
printf(" trigger: %d", psampcol->trigger_mode);
if (psampcol->trigger_mode == SVMP_SAMPCOL_TRIGGER_PKTPROC_TRANSITION) {
printf(", %d -> %d\n", psampcol->trigger_mode_s[0], psampcol->trigger_mode_s[1]);
} else {
printf("\n");
}
printf(" waitcnt: %d\n", psampcol->waitcnt);
printf(" caplen: %d\n", psampcol->caplen);
printf(" samplerate: %dx\n", (psampcol->data_samplerate + 1));
printf(" data: %s", str_phy1mux[psampcol->data_sel_phy1]);
if (psampcol->data_sel_phy1 == SVMP_SAMPCOL_PHY1MUX_RX1MUX) {
printf(" with %s\n",
str_rx1mux[psampcol->data_sel_rx1-SVMP_SAMPCOL_RX1MUX_FARROWOUT]);
} else {
printf("\n");
}
printf(" dualcap: ");
if (psampcol->data_sel_dualcap >= SVMP_SAMPCOL_RX1MUX_FARROWOUT) {
printf("%s\n",
str_rx1mux[psampcol->data_sel_dualcap-SVMP_SAMPCOL_RX1MUX_FARROWOUT]);
} else {
printf("OFF\n");
}
printf(" pack: %s, %s-endian",
str_packmode[psampcol->pack_mode], str_packorder[psampcol->pack_order]);
if (psampcol->pack_mode == SVMP_SAMPCOL_PACK_1CORE) {
printf(" core%d\n", psampcol->pack_1core_sel);
} else {
printf("\n");
}
printf(" buf_addr: 0x%05x~0x%05x\n", psampcol->buff_addr_start, psampcol->buff_addr_end);
if (mode == 0) {
printf(" status: overflow=%d, running=%d\n",
psampcol->status&0x1, (psampcol->status&0x2)>>1);
}
}
static int
wl_svmp_sampcol(void *wl, cmd_t *cmd, char **argv)
{
int n;
int ret;
wl_svmp_sampcol_t sampcol;
uint16 cmd_nparam;
uint16 cmd_enable;
wlc_rev_info_t revinfo;
uint32 phytype;
uint32 phyrev;
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);
if ((phytype != WLC_PHY_TYPE_AC) || ((phyrev != 32) && (phyrev != 33))) {
return BCME_USAGE_ERROR;
}
sampcol.version = WL_SVMP_SAMPCOL_PARAMS_VERSION;
sampcol.enable = 0;
sampcol.trigger_mode = SVMP_SAMPCOL_TRIGGER_PKTPROC_TRANSITION;
sampcol.trigger_mode_s[0] = SVMP_SAMPCOL_PKTPROC_OFDM_PHY;
sampcol.trigger_mode_s[1] = SVMP_SAMPCOL_PKTPROC_TIMING_SEARCH;
sampcol.waitcnt = 0;
sampcol.caplen = 128;
sampcol.data_samplerate = SVMP_SAMPCOL_SAMPLERATE_2XBW;
sampcol.data_sel_phy1 = SVMP_SAMPCOL_PHY1MUX_RX1MUX;
sampcol.data_sel_rx1 = SVMP_SAMPCOL_RX1MUX_FARROWOUT;
sampcol.data_sel_dualcap = 0;
sampcol.pack_mode = SVMP_SAMPCOL_PACK_4CORE;
sampcol.pack_order = 0;
sampcol.pack_cfix_fmt = 0;
sampcol.pack_1core_sel = 0;
sampcol.buff_addr_start = 0x25800;
sampcol.buff_addr_end = 0x26800;
sampcol.int2vasip = 1;
ret = -1;
cmd_nparam = 0;
cmd_enable = 0;
argv++;
while (*argv) {
char *s = *argv++;
if (*argv == NULL) {
ret = BCME_USAGE_ERROR;
goto exit_svmp_sampcol;
}
if (!strcmp(s, "-h")) {
ret = BCME_USAGE_ERROR;
goto exit_svmp_sampcol;
} else if (!strcmp(s, "-e")) {
cmd_nparam++;
sampcol.enable = (uint8)strtol(*argv++, NULL, 0);
cmd_enable = 1;
} else if (!strcmp(s, "-t")) {
cmd_nparam++;
sampcol.trigger_mode = (uint8)strtol(*argv++, NULL, 0);
if (sampcol.trigger_mode == SVMP_SAMPCOL_TRIGGER_PKTPROC_TRANSITION) {
for (n = 0; n < 2; n++) {
if (*argv == NULL) {
ret = BCME_USAGE_ERROR;
goto exit_svmp_sampcol;
} else {
sampcol.trigger_mode_s[n]
= (uint8)strtol(*argv++, NULL, 0);
}
}
}
} else if (!strcmp(s, "-w")) {
cmd_nparam++;
sampcol.waitcnt = (uint16)strtol(*argv++, NULL, 0);
} else if (!strcmp(s, "-l")) {
cmd_nparam++;
sampcol.caplen = (uint16)strtol(*argv++, NULL, 0);
} else if (!strcmp(s, "-s")) {
cmd_nparam++;
sampcol.data_sel_phy1 = (uint8)strtol(*argv++, NULL, 0);
if ((sampcol.data_sel_phy1 == SVMP_SAMPCOL_PHY1MUX_RX1MUX) &&
strncmp(*argv, "-", 1)) {
sampcol.data_sel_rx1 = (uint8)strtol(*argv++, NULL, 0);
}
} else if (!strcmp(s, "-d")) {
cmd_nparam++;
sampcol.data_sel_dualcap = (uint8)strtol(*argv++, NULL, 0);
} else if (!strcmp(s, "-r")) {
cmd_nparam++;
sampcol.data_samplerate = (uint8)strtol(*argv++, NULL, 0);
} else if (!strcmp(s, "-p")) {
cmd_nparam++;
sampcol.pack_mode = (uint8)strtol(*argv++, NULL, 0);
if (strncmp(*argv, "-", 1)) {
sampcol.pack_order = (uint8)strtol(*argv++, NULL, 0);
}
if (strncmp(*argv, "-", 1)) {
sampcol.pack_cfix_fmt = (uint8)strtol(*argv++, NULL, 0);
}
if ((phytype == WLC_PHY_TYPE_AC) && (phyrev == 33) &&
(sampcol.pack_mode == SVMP_SAMPCOL_PACK_1CORE)) {
if (strncmp(*argv, "-", 1)) {
sampcol.pack_1core_sel = (uint8)strtol(*argv++, NULL, 0);
}
}
} else if (!strcmp(s, "-a")) {
cmd_nparam++;
sampcol.buff_addr_start = (uint32)strtol(*argv++, NULL, 0);
if (strncmp(*argv, "-", 1)) {
sampcol.buff_addr_end = (uint32)strtol(*argv++, NULL, 0);
}
} else {
argv++;
}
}
if ((cmd_nparam == 0) || ((cmd_enable == 1) && (cmd_nparam == 1))) {
cmd_enable = sampcol.enable;
ret = wlu_iovar_get(wl, cmd->name, &sampcol, sizeof(wl_svmp_sampcol_t));
if (sampcol.version != WL_SVMP_SAMPCOL_PARAMS_VERSION) {
printf("\tIncorrect version "
"of SVMP_SAMPCOL_PARAMS struct: expect %d but get %d\n",
WL_SVMP_SAMPCOL_PARAMS_VERSION, sampcol.version);
return BCME_BADARG;
}
if (cmd_nparam == 1) {
sampcol.enable = cmd_enable;
} else {
wl_svmp_sampcol_printf_configs(&sampcol, 0);
}
}
if (cmd_nparam > 0) {
wl_svmp_sampcol_printf_configs(&sampcol, 1);
ret = wlu_var_setbuf(wl, cmd->name, &sampcol, sizeof(wl_svmp_sampcol_t));
}
exit_svmp_sampcol:
return ret;
}
#endif
static int
wl_scanmac(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
char *subcmd;
int subcmd_len;
/* skip iovar */
argv++;
/* must have subcommand */
subcmd = *argv++;
if (!subcmd) {
return BCME_USAGE_ERROR;
}
subcmd_len = strlen(subcmd);
if (!*argv) {
/* get */
uint8 buffer[OFFSETOF(wl_scanmac_t, data)];
wl_scanmac_t *sm = (wl_scanmac_t *)buffer;
int len = OFFSETOF(wl_scanmac_t, data);
memset(sm, 0, len);
if (!strncmp(subcmd, "enable", subcmd_len)) {
sm->subcmd_id = WL_SCANMAC_SUBCMD_ENABLE;
} else if (!strncmp(subcmd, "bsscfg", subcmd_len)) {
sm->subcmd_id = WL_SCANMAC_SUBCMD_BSSCFG;
} else if (!strncmp(subcmd, "config", subcmd_len)) {
sm->subcmd_id = WL_SCANMAC_SUBCMD_CONFIG;
} else {
return BCME_USAGE_ERROR;
}
/* invoke GET iovar */
sm->subcmd_id = dtoh16(sm->subcmd_id);
sm->len = dtoh16(sm->len);
if ((err = wlu_iovar_getbuf(wl, cmd->name, sm, len, buf, WLC_IOCTL_SMLEN)) < 0) {
return err;
}
/* process and print GET results */
sm = (wl_scanmac_t *)buf;
sm->subcmd_id = dtoh16(sm->subcmd_id);
sm->len = dtoh16(sm->len);
switch (sm->subcmd_id) {
case WL_SCANMAC_SUBCMD_ENABLE:
{
wl_scanmac_enable_t *sm_enable = (wl_scanmac_enable_t *)sm->data;
if (sm->len >= sizeof(*sm_enable)) {
printf("%d\n", sm_enable->enable);
} else {
err = BCME_BADLEN;
}
break;
}
case WL_SCANMAC_SUBCMD_BSSCFG:
{
wl_scanmac_bsscfg_t *sm_bsscfg = (wl_scanmac_bsscfg_t *)sm->data;
if (sm->len >= sizeof(*sm_bsscfg)) {
sm_bsscfg->bsscfg = dtoh32(sm_bsscfg->bsscfg);
printf("%d\n", sm_bsscfg->bsscfg);
} else {
err = BCME_BADLEN;
}
break;
}
case WL_SCANMAC_SUBCMD_CONFIG:
{
wl_scanmac_config_t *sm_config = (wl_scanmac_config_t *)sm->data;
if (sm->len >= sizeof(*sm_config)) {
sm_config->scan_bitmap = dtoh16(sm_config->scan_bitmap);
printf("mac: %s\n", wl_ether_etoa(&sm_config->mac));
printf("random mask: %s\n", wl_ether_etoa(&sm_config->random_mask));
printf("scan bitmap: 0x%02X\n", sm_config->scan_bitmap);
if (sm_config->scan_bitmap & WL_SCANMAC_SCAN_UNASSOC) {
printf(" unassoc\n");
}
if (sm_config->scan_bitmap & WL_SCANMAC_SCAN_ASSOC_ROAM) {
printf(" assoc roam\n");
}
if (sm_config->scan_bitmap & WL_SCANMAC_SCAN_ASSOC_PNO) {
printf(" assoc PNO\n");
}
if (sm_config->scan_bitmap & WL_SCANMAC_SCAN_ASSOC_HOST) {
printf(" assoc host\n");
}
} else {
err = BCME_BADLEN;
}
break;
}
default:
break;
}
}
else {
/* set */
uint8 buffer[OFFSETOF(wl_scanmac_t, data) +
MAX(sizeof(wl_scanmac_enable_t), sizeof(wl_scanmac_config_t))];
wl_scanmac_t *sm = (wl_scanmac_t *)buffer;
int len = OFFSETOF(wl_scanmac_t, data);
if (!strncmp(subcmd, "enable", subcmd_len) &&
(*argv[0] == '0' || *argv[0] == '1')) {
wl_scanmac_enable_t *sm_enable = (wl_scanmac_enable_t *)sm->data;
sm->subcmd_id = WL_SCANMAC_SUBCMD_ENABLE;
sm->len = sizeof(*sm_enable);
sm_enable->enable = atoi(argv[0]);
} else if (!strncmp(subcmd, "config", subcmd_len) &&
argv[0] && argv[1] && argv[2]) {
wl_scanmac_config_t *sm_config = (wl_scanmac_config_t *)sm->data;
char *mac = argv[0];
char *mask = argv[1];
char *bitmap = argv[2];
sm->subcmd_id = WL_SCANMAC_SUBCMD_CONFIG;
sm->len = sizeof(*sm_config);
if (!wl_ether_atoe(mac, &sm_config->mac) ||
!wl_ether_atoe(mask, &sm_config->random_mask)) {
return BCME_USAGE_ERROR;
}
sm_config->scan_bitmap = (uint16)strtoul(bitmap, NULL, 0);
sm_config->scan_bitmap = htod16(sm_config->scan_bitmap);
} else {
return BCME_USAGE_ERROR;
}
/* invoke SET iovar */
len = OFFSETOF(wl_scanmac_t, data) + sm->len;
sm->subcmd_id = htod16(sm->subcmd_id);
sm->len = htod16(sm->len);
err = wlu_iovar_set(wl, cmd->name, sm, len);
}
return err;
}
static int wl_winver(void *wl, cmd_t *cmd, char **argv)
{
int err = 0;
wl_winver_t wv;
UNUSED_PARAMETER(argv);
memset(&wv, 0, sizeof(wv));
err = wlu_iovar_get(wl, cmd->name, &wv, sizeof(wv));
if ((err == BCME_OK) && (wv.struct_version >= WL_WINVER_STRUCT_VER_1)) {
printf("\n"
"Windows OS ver= %d.%d.%d\n"
"NDIS Runtime = %d.%d\n"
"NDIS Driver = %d.%d\n"
"WDI UE version= %d.%d_%X\n"
"WDI LE version= %d.%d_%X\n",
wv.os_runtime.major_ver, wv.os_runtime.minor_ver, wv.os_runtime.build,
wv.ndis_runtime.major_ver, wv.ndis_runtime.minor_ver,
wv.ndis_driver.major_ver, wv.ndis_driver.minor_ver,
wv.wdi_ue.major_ver, wv.wdi_ue.minor_ver, wv.wdi_ue.suffix,
wv.wdi_le.major_ver, wv.wdi_le.minor_ver, wv.wdi_le.suffix);
}
return err;
}
#define TCMS_MAX_CORES (8)
#define TCMS_INPUT_BUFSIZE (1 + 1 + 1)
static int
wl_get_tcmstbl_entry(void *wl, cmd_t *cmd, char **argv)
{
int32 wl_tcmsarg[TCMS_INPUT_BUFSIZE] = {0, 0, 0};
int32* obuf = (int32 *)buf;
int err, opt_err;
miniopt_t to;
const char* fn_name = "wl_get_tcmstbl_entry";
int argc = 0;
UNUSED_PARAMETER(cmd);
/* counting the number of arguments */
while (argv[argc])
argc++;
if (argc < 2)
return BCME_USAGE_ERROR;
memset(buf, 0, WLC_IOCTL_MEDLEN);
/* 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 == 'c') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as an int for core\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val > (TCMS_MAX_CORES-1)) {
fprintf(stderr,
"%s: invalid core %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
wl_tcmsarg[0] = to.val;
}
if (to.opt == 'a') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as"
" an int for antenna map\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
wl_tcmsarg[1] = to.val;
}
if (to.opt == 's') {
if (!to.good_int) {
fprintf(stderr,
"%s: could not parse \"%s\" as"
" an int for cell status\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if ((to.val < 0) | (to.val > 1)) {
fprintf(stderr,
"%s: invalid cell status %d\n",
fn_name, to.val);
err = BCME_BADARG;
goto exit;
}
wl_tcmsarg[2] = to.val;
}
}
} else {
err = BCME_BADARG;
goto exit;
}
if ((err = wlu_iovar_getbuf(wl, "tcmstbl", wl_tcmsarg, sizeof(wl_tcmsarg),
buf, WLC_IOCTL_MEDLEN)) < 0) {
printf("\n Incorrect option \n");
err = BCME_BADOPTION;
goto exit;
}
printf("(Nsts {4..1}) { %d hdBm 0x%02x } { %d hdBm 0x%02x }"
"{ %d hdBm 0x%02x } { %d hdBm 0x%02x }\n",
obuf[10], obuf[11], obuf[8], obuf[9],
obuf[6], obuf[7], obuf[4], obuf[5]);
printf("OFDM { %d hdBm 0x%02x } CCK { %d hdBm 0x%02x }\n",
obuf[2], obuf[3], obuf[0], obuf[1]);
exit:
return err;
}
/*
* get/set IPv6 RA rate limit interval
*/
static int
wl_nd_ra_limit_intv(void *wl, cmd_t *cmd, char **argv)
{
int err = 0, opt_err;
miniopt_t to;
nd_ra_ol_limits_t *ra_limit = NULL;
UNUSED_PARAMETER(cmd);
if ((err = wlu_var_getbuf(wl,
"nd_ra_limit_intv", NULL, 0, (void *)&ra_limit) < 0)) {
printf("nd_ra_limit_intv: getbuf ioctl failed\n");
return err;
}
if (*++argv) {
/* set */
if (argv[0][0] == 'h' ||
argv[0][0] == '?') {
err = BCME_USAGE_ERROR;
goto exit;
}
ra_limit = calloc(1, sizeof(nd_ra_ol_limits_t));
if (ra_limit == NULL) {
return BCME_NOMEM;
}
miniopt_init(&to, __FUNCTION__, 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 == 't') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" type\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
ra_limit->type = to.val;
}
if (to.opt == 'p') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" percentage value\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
ra_limit->limits.lifetime_relative.lifetime_percent = to.val;
}
if (to.opt == 'm') {
if (!to.good_int) {
fprintf(stderr, "could not parse %s as an int for"
" min_time\n", to.valstr);
err = BCME_BADARG;
goto exit;
}
ra_limit->limits.lifetime_relative.min_time = to.val;
}
}
ra_limit->version = ND_RA_OL_LIMITS_VER;
if (ra_limit->type == ND_RA_OL_LIMITS_REL_TYPE) {
printf("type %d, lifetime_percent %d, min_time %d\n",
ra_limit->type,
ra_limit->limits.lifetime_relative.lifetime_percent,
ra_limit->limits.lifetime_relative.min_time);
ra_limit->length = ND_RA_OL_LIMITS_REL_TYPE_LEN;
} else if (ra_limit->type == ND_RA_OL_LIMITS_FIXED_TYPE) {
printf("type %d, min_time %d\n",
ra_limit->type,
ra_limit->limits.fixed.hold_time);
ra_limit->length = ND_RA_OL_LIMITS_FIXED_TYPE_LEN;
} else {
printf("Invalid nd_ra_limit_intv type %d\n",
ra_limit->type);
err = BCME_BADARG;
goto exit;
}
if ((err = wlu_var_setbuf(wl, "nd_ra_limit_intv", ra_limit,
sizeof(nd_ra_ol_limits_t)) < 0)) {
printf("%s: failed to set %d\n", cmd->name, err);
}
} else {
/* get */
if (ra_limit->version != ND_RA_OL_LIMITS_VER) {
printf("Invalid nd_ra_limit_intv version %d\n",
ra_limit->version);
err = BCME_VERSION;
goto exit;
}
if (ra_limit->type == ND_RA_OL_LIMITS_REL_TYPE) {
printf(" type %d, percentage %d, fixed time %d\n",
ra_limit->type,
ra_limit->limits.lifetime_relative.lifetime_percent,
ra_limit->limits.lifetime_relative.min_time);
} else if (ra_limit->type == ND_RA_OL_LIMITS_FIXED_TYPE) {
printf(" type %d, fixed time %d\n",
ra_limit->type,
ra_limit->limits.fixed.hold_time);
} else {
printf("Invalid nd_ra_limit_intv type %d\n",
ra_limit->type);
err = BCME_BADARG;
goto exit;
}
}
exit:
if (ra_limit) {
free(ra_limit);
}
return err;
}
int
wl_ccode_info(void *wl, cmd_t *cmd, char **argv)
{
int error, i;
void *ptr;
const char* abbrev;
wl_ccode_info_t *ci;
wl_ccode_entry_t *ce;
cntry_name_t *cntry;
const char *band_type[] = {"2G", "5G"};
const char *ccode_type[] = {"accode", "hdcode", "11dassoccode", "11dscancode", "defccode"};
/* skip the command name */
argv++;
if ((error = wlu_var_getbuf(wl, cmd->name, NULL, 0, &ptr)) < 0)
return (error);
ci = (wl_ccode_info_t *)ptr;
if (dtoh16(ci->version) != CCODE_INFO_VERSION) {
printf("\tIncorrect version of ccode_info IOVAR expected %d; got %d\n",
CCODE_INFO_VERSION, dtoh16(ci->version));
return -1;
}
if (dtoh16(ci->count) > 2 * WLC_NUM_CCODE_INFO) {
printf("\tBigger than expected country codes. Expected:%d; got %d\n",
2 * WLC_NUM_CCODE_INFO, dtoh16(ci->count));
return -1;
}
for (i = 0; i < dtoh16(ci->count); i++) {
ce = &ci->ccodelist[i];
abbrev = ce->ccode;
cntry = wlc_cntry_abbrev_to_country(abbrev);
printf("%s\t%s:%s\t%s\n",
band_type[ce->band],
ccode_type[ce->role],
abbrev, cntry ? cntry->name : "");
}
return 0;
}
#if defined(linux)
int
wl_wait_for_event(void *wl, char **argv, uint event_id, uint evbuf_size,
void (*event_cb_fn)(int event_type, bcm_event_t *bcm_event))
{
int err = BCME_OK;
int fd, octets;
struct sockaddr_ll sll;
struct ifreq ifr;
char ifnames[IFNAMSIZ] = {"eth1"};
uint8 event_buf[WL_EVENTINT_MAX_GET_SIZE];
eventmsgs_ext_t *eventmsgs;
char *data;
/* 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));
}
ifnames[IFNAMSIZ - 1] = '\0';
/* set event bit using 'event_msgs_ext' */
if ((event_id / 8) >= WL_EVENTING_MASK_MAX_LEN) {
printf("Event Id %d exceeds max %d event bytes\n",
event_id, WL_EVENTING_MASK_MAX_LEN);
goto exit2;
}
memset(event_buf, 0, sizeof(event_buf));
eventmsgs = (eventmsgs_ext_t *)event_buf;
eventmsgs->ver = EVENTMSGS_VER;
eventmsgs->command = EVENTMSGS_SET_BIT;
eventmsgs->len = WL_EVENTING_MASK_MAX_LEN;
eventmsgs->mask[event_id / 8] |= 1 << (event_id % 8);
if ((err = wlu_var_setbuf(wl, "event_msgs_ext", eventmsgs,
EVENTMSGS_EXT_STRUCT_SIZE + eventmsgs->len)) < 0) {
printf("Failed to set event mask\n");
goto exit2;
}
/* Open a socket to read driver WLC_E_* events */
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));
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("%s: Cannot get index %d\n", __FUNCTION__, 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(evbuf_size);
if (data == NULL) {
printf("Cannot not allocate %u bytes for events receive buffer\n",
evbuf_size);
err = -1;
goto exit1;
}
/* Loop forever to receive driver events */
while (1) {
bcm_event_t *bcm_event;
int event_type;
octets = recv(fd, data, evbuf_size, 0);
bcm_event = (bcm_event_t *)data;
event_type = ntoh32(bcm_event->event.event_type);
if (octets >= (int)sizeof(bcm_event_t)) {
event_cb_fn(event_type, bcm_event);
}
}
free(data);
exit1:
close(fd);
exit2:
return err;
}
#endif /* linux */
#define SIM_PM_MAX_CYCLE 10000
#define SIM_PM_MAX_UP 1000
#define SIM_PM_DEF_CYCLE 100
#define SIM_PM_DEF_UP 5
static int
wl_sim_pm(void *wl, cmd_t *cmd, char **argv)
{
int err = BCME_OK, opt_err;
miniopt_t to;
const char* fn_name = "wl_sim_pm";
sim_pm_params_t *params = (sim_pm_params_t *)buf;
if ((err = wlu_iovar_getbuf(wl, cmd->name, NULL, 0, buf, WLC_IOCTL_SMLEN)) < 0) {
printf("Get %s got error %d\n", cmd->name, err);
goto exit;
}
if (!*++argv) {
printf("%sabled, Cycle time: %u TUs, Up time: %u TUs\n", params->enabled ? "En" :
"Dis", params->cycle, params->up);
goto exit;
}
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 == '\0') {
if (!to.good_int) {
fprintf(stderr, "%s: could not parse \"%s\" as an int for enable\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val != 0 && to.val != 1) {
fprintf(stderr, "%s: invalid enable parameter. [0|1]\n", fn_name);
err = BCME_BADARG;
goto exit;
}
params->enabled = to.val;
}
if (to.opt == 'c') {
if (!to.good_int) {
fprintf(stderr, "%s: could not parse \"%s\" as an int for cycle\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val <= 0 || to.val > SIM_PM_MAX_CYCLE) {
fprintf(stderr, "%s: invalid cycle time. Max. %d\n", fn_name,
SIM_PM_MAX_CYCLE);
err = BCME_BADARG;
goto exit;
}
params->cycle = to.val;
}
if (to.opt == 'u') {
if (!to.good_int) {
fprintf(stderr, "%s: could not parse \"%s\" as an int for up\n",
fn_name, to.valstr);
err = BCME_BADARG;
goto exit;
}
if (to.val <= 0 || to.val > SIM_PM_MAX_UP) {
fprintf(stderr, "%s: invalid up time. Max. %d\n", fn_name,
SIM_PM_MAX_UP);
err = BCME_BADARG;
goto exit;
}
params->up = to.val;
}
}
params->cycle = params->cycle ? params->cycle : SIM_PM_DEF_CYCLE;
params->up = params->up ? params->up : SIM_PM_DEF_UP;
if (params->up >= params->cycle) {
fprintf(stderr, "%s: cycle time (%d) should be greater than up time (%d).\n",
fn_name, params->cycle, params->up);
err = BCME_BADARG;
goto exit;
}
if ((err = wlu_iovar_set(wl, cmd->name, params, sizeof(*params))) < 0) {
printf("Error setting variable %s\n", cmd->name);
return err;
}
exit:
return err;
}
/*
* Health Check "hc" iovar support
*/
/* hc command information structure, forward decl. */
struct hc_sub_cmd_ent;
/* hc sub-command handler function, used in the hc command tables */
typedef int (*hc_cmd_fn_t)(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, char **argv);
/* hc command information structure, definition
* Used to dispatch action based on a sub-command name
*/
struct hc_sub_cmd_ent {
const char *name; /* sub-command name for command line */
uint16 id; /* cmd id for the handler fn */
hc_cmd_fn_t fn; /* sub-command handler fn */
};
typedef struct hc_sub_cmd_ent hc_sub_cmd_table_t[];
static struct hc_sub_cmd_ent* wl_hc_cmd_lookup(hc_sub_cmd_table_t tbl, char* cmd);
static void wl_hc_subcommand_list_dump(void *wl, const char *category, hc_sub_cmd_table_t tbl);
static int wl_hc_int(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, char** argv);
static int wl_hc_exclude_bitmap(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd,
char** argv);
static int wl_hc_setints(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd,
int int_count, char **argv);
static int wl_hc_getints(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd,
int int_count, char **argv);
/* HC Tx category commands */
struct hc_sub_cmd_ent hc_tx_cmds[] = {
{"stall_threshold", WL_HC_TX_XTLV_ID_VAL_STALL_THRESHOLD, wl_hc_int},
{"stall_sample_size", WL_HC_TX_XTLV_ID_VAL_STALL_SAMPLE_SIZE, wl_hc_int},
{"stall_timeout", WL_HC_TX_XTLV_ID_VAL_STALL_TIMEOUT, wl_hc_int},
{"stall_force", WL_HC_TX_XTLV_ID_VAL_STALL_FORCE, wl_hc_int},
{"stall_exclude", WL_HC_TX_XTLV_ID_VAL_STALL_EXCLUDE, wl_hc_exclude_bitmap},
{"fc_timeout", WL_HC_TX_XTLV_ID_VAL_FC_TIMEOUT, wl_hc_int},
{"fc_force", WL_HC_TX_XTLV_ID_VAL_FC_FORCE, wl_hc_int},
/* NUL terminated table */
{NULL, 0, NULL},
};
/* HC Rx category commands */
hc_sub_cmd_table_t hc_rx_cmds = {
{"dma_stall_timeout", WL_HC_RX_XTLV_ID_VAL_DMA_STALL_TIMEOUT, wl_hc_int},
{"dma_stall_force", WL_HC_RX_XTLV_ID_VAL_DMA_STALL_FORCE, wl_hc_int},
{"stall_threshold", WL_HC_RX_XTLV_ID_VAL_STALL_THRESHOLD, wl_hc_int},
{"stall_sample_size", WL_HC_RX_XTLV_ID_VAL_STALL_SAMPLE_SIZE, wl_hc_int},
{"stall_force", WL_HC_RX_XTLV_ID_VAL_STALL_FORCE, wl_hc_int},
/* NUL terminated table */
{NULL, 0, NULL},
};
/* Main command line handler for Health Check 'hc' iovar */
static int
wl_hc(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
char *sub_cmd_name;
struct hc_sub_cmd_ent *sub_cmd;
UNUSED_PARAMETER(cmd);
/* Skip the command name */
argv++;
sub_cmd_name = *argv++;
if (sub_cmd_name == NULL) {
/* usage */
printf("hc: missing category\n");
return BCME_USAGE_ERROR;
}
if (!strcmp(sub_cmd_name, "tx")) {
char* attr_name;
attr_name = *argv++;
if (attr_name == NULL) {
/* usage */
printf("hc tx: missing attribute\n");
wl_hc_subcommand_list_dump(wl, sub_cmd_name, hc_tx_cmds);
return BCME_USAGE_ERROR;
}
/* grab the command info from the sub-command table */
sub_cmd = wl_hc_cmd_lookup(hc_tx_cmds, attr_name);
if (sub_cmd == NULL) {
/* usage */
printf("hc tx: unknown sub-command \"%s\"\n", attr_name);
wl_hc_subcommand_list_dump(wl, sub_cmd_name, hc_tx_cmds);
return BCME_USAGE_ERROR;
}
/* execute the sub command */
ret = (*sub_cmd->fn)(wl, WL_HC_XTLV_ID_CAT_DATAPATH_TX, sub_cmd, argv);
} else if (!strcmp(sub_cmd_name, "rx")) {
char* attr_name;
attr_name = *argv++;
if (attr_name == NULL) {
/* usage */
printf("hc rx: missing attribute\n");
wl_hc_subcommand_list_dump(wl, sub_cmd_name, hc_rx_cmds);
return BCME_USAGE_ERROR;
}
/* grab the command info from the sub-command table */
sub_cmd = wl_hc_cmd_lookup(hc_rx_cmds, attr_name);
if (sub_cmd == NULL) {
/* usage */
printf("hc rx: unknown sub-command \"%s\"\n", attr_name);
wl_hc_subcommand_list_dump(wl, sub_cmd_name, hc_rx_cmds);
return BCME_USAGE_ERROR;
}
/* execute the sub command */
ret = (*sub_cmd->fn)(wl, WL_HC_XTLV_ID_CAT_DATAPATH_RX, sub_cmd, argv);
} else {
/* usage */
printf("unknown hc category \"%s\"\n", sub_cmd_name);
return BCME_USAGE_ERROR;
}
return ret;
}
/* Generic set/get of an integer attribute.
* Just issue a wl_hc_setints() if there is a 2nd arg, or wl_hc_getints() otherwise.
*/
static int
wl_hc_int(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, char** argv)
{
if (argv[0] != NULL)
return (wl_hc_setints(wl, category, hc_cmd, 1, argv));
else
return (wl_hc_getints(wl, category, hc_cmd, 1, argv));
}
/* Set/Get of the "exclude" bitmap.
* Just issue a wl_hc_setints() for 2 ints if there is a 2nd arg,
* or wl_hc_getints() for 2 ints otherwise.
*/
static int
wl_hc_exclude_bitmap(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, char** argv)
{
if (argv[0] != NULL)
return (wl_hc_setints(wl, category, hc_cmd, 2, argv));
else
return (wl_hc_getints(wl, category, hc_cmd, 2, argv));
}
static int
wl_hc_setints(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, int int_count, char **argv)
{
char *valstr;
char *endptr = NULL;
bcm_xtlv_t *hc_tlv;
struct {
uint16 id;
uint16 len;
uint32 value[1];
} *val_xtlv;
uint32 words[2];
int i;
uint out_len;
uint16 val_payload_len;
uint16 container_payload_len;
uint32 val;
int ret = 0;
for (i = 0; i < int_count; i++) {
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("hc set: missing value argument for set of \"%s\"\n", hc_cmd->name);
return BCME_USAGE_ERROR;
}
val = strtoul(valstr, &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",
valstr, hc_cmd->name);
return BCME_USAGE_ERROR;
}
words[i] = val;
}
/* total output buffer will be an hc sub-category xtlv holding
* an xtvl of 1 or 2 uint32s.
*/
val_payload_len = (int_count * sizeof(val_xtlv->value[0]));
container_payload_len = bcm_xtlv_size_for_data(val_payload_len,
BCM_XTLV_OPTION_NONE);
out_len = bcm_xtlv_size_for_data(container_payload_len,
BCM_XTLV_OPTION_NONE);
/* init the wrapper hc category XTLV in the IO buffer */
hc_tlv = (bcm_xtlv_t*)(&buf[0]);
hc_tlv->id = htol16(category);
hc_tlv->len = htol16(container_payload_len);
val_xtlv = (void*)hc_tlv->data;
val_xtlv->id = htol16(hc_cmd->id);
val_xtlv->len = htol16(val_payload_len);
for (i = 0; i < int_count; i++) {
val_xtlv->value[i] = htol32(words[i]);
}
ret = wlu_iovar_set(wl, "hc", hc_tlv, out_len);
return ret;
}
static int
wl_hc_getints(void *wl, uint16 category, struct hc_sub_cmd_ent *hc_cmd, int int_count, char **argv)
{
int ret = 0;
bcm_xtlv_t *hc_tlv;
bcm_xtlv_t *val_xtlv;
uint16 hc_len, hc_id;
uint16 val_len, val_id;
struct {
uint16 id;
uint16 len;
uint16 ids[1];
} id_list;
uint param_len;
const bcm_xtlv_opts_t no_pad = BCM_XTLV_OPTION_NONE;
if (*argv) {
printf("hc get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
/* total param buffer will be an hc sub-category xtlv holding
* an xtvl ID list of one element.
* no padding since the id_list is the last element.
*/
param_len = bcm_xtlv_size_for_data(sizeof(id_list), no_pad);
/* init an XTLV ID list with the desired attribute ID */
bcm_xtlv_pack_xtlv((bcm_xtlv_t *)&id_list, WL_HC_XTLV_ID_IDLIST, sizeof(uint16),
NULL, no_pad);
id_list.ids[0] = htol16(hc_cmd->id);
/* init the wrapper hc category XTLV in the IO buffer and copy to it */
hc_tlv = (bcm_xtlv_t *)&buf[0];
bcm_xtlv_pack_xtlv(hc_tlv, category, sizeof(id_list), (const uint8 *)&id_list, no_pad);
/* issue the 'hc' get with the one-item id list */
if ((ret = wlu_iovar_getbuf(wl, "hc", buf, param_len, buf, WLC_IOCTL_SMLEN)))
return ret;
hc_len = ltoh16(hc_tlv->len);
hc_id = ltoh16(hc_tlv->id);
/* make sure the return xtlv is valid for the iobuffer */
if (!bcm_valid_xtlv(hc_tlv, WLC_IOCTL_SMLEN, no_pad)) {
printf("hc get: return XTLV specifies length %u too long for iobuffer len %u\n",
hc_len, WLC_IOCTL_SMLEN);
return BCME_ERROR;
}
/* the return buf should start with the HC xtlv container */
if (hc_id != category) {
printf("hc get: return category %u did not match %u\n",
hc_id, category);
return BCME_ERROR;
}
/* make sure the container has enough room for an XTLV header */
if (hc_len < BCM_XTLV_HDR_SIZE) {
printf("hc get: return XTLV container specifies length %u too small "
"to hold any values\n",
hc_len);
return BCME_ERROR;
}
val_xtlv = (bcm_xtlv_t*)hc_tlv->data;
val_len = ltoh16(val_xtlv->len);
val_id = ltoh16(val_xtlv->id);
/* make sure the value xtlv is valid xtlv container */
if (!bcm_valid_xtlv(val_xtlv, hc_len, no_pad)) {
printf("hc get: return XTLV specifies length %u too long "
"for containing xtlv len %u\n",
val_len, hc_len);
return BCME_ERROR;
}
if (val_len == 0) {
printf("hc get: return value XTLV (id:%u) empty\n", val_id);
return BCME_ERROR;
} else if (val_len != int_count * sizeof(int32)) {
printf("hc get: WARNING: return value XTLV (id:%u len:%u) was not "
"the expected len %u\n",
val_id, val_len, (uint)(int_count * sizeof(int32)));
ret = BCME_ERROR;
}
/* print as words if multiple of word size, otherwise hexdump */
if (val_len % sizeof(uint32) == 0) {
uint32 *w = (uint32*)val_xtlv->data;
int i;
for (i = 0; i * sizeof(uint32) < val_len; i++) {
wl_printint((int)ltoh32(w[i]));
}
} else {
wl_hexdump((uint8*)val_xtlv->data, val_len);
}
return (ret);
}
static struct hc_sub_cmd_ent*
wl_hc_cmd_lookup(hc_sub_cmd_table_t tbl, char* cmd)
{
struct hc_sub_cmd_ent* entry;
for (entry = tbl; entry->name != NULL; entry++) {
if (!strcmp(entry->name, cmd)) {
break;
}
}
if (entry->name == NULL) {
entry = NULL;
}
return entry;
}
static void
wl_hc_subcommand_list_dump(void *wl, const char *category, hc_sub_cmd_table_t tbl)
{
struct hc_sub_cmd_ent* entry;
BCM_REFERENCE(wl);
printf("Sub-Commands for \"%s\":\n", category);
for (entry = tbl; entry->name != NULL; entry++) {
printf(" %s %s\n", category, entry->name);
}
}
static int
wl_idauth(void *wl, cmd_t *cmd, char **argv)
{
int ret = 0;
char *sub_cmd_name;
UNUSED_PARAMETER(cmd);
/* Skip the command name */
argv++;
sub_cmd_name = *argv++;
if (sub_cmd_name == NULL) {
/* usage */
printf("idauth: missing category\n");
return BCME_USAGE_ERROR;
}
if (!strcmp(sub_cmd_name, "config")) {
char* attr_name;
attr_name = *argv;
if (attr_name == NULL) {
/* GET IDAUTH CONFIG */
ret = wl_idauth_config_get(wl, WL_IDAUTH_CMD_CONFIG, argv);
}
else {
/* SET AUTH Config */
ret = wl_idauth_config_set(wl, WL_IDAUTH_CMD_CONFIG, argv);
}
} else if (!strcmp(sub_cmd_name, "peer_info")) {
if (*argv == NULL) {
ret = wl_idauth_dump_peer_info(wl, WL_IDAUTH_CMD_PEER_INFO, argv);
} else {
printf("auth peer_info get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
} else if (!strcmp(sub_cmd_name, "counters")) {
if (*argv == NULL) {
ret = wl_idauth_dump_counters(wl, WL_IDAUTH_CMD_COUNTERS, argv);
} else {
printf("auth counters get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
} else {
/* usage */
printf("unknown idauth sub-commands \"%s\"\n", sub_cmd_name);
return BCME_USAGE_ERROR;
}
return ret;
}
static int
wl_idauth_config_set(void *wl, uint16 category, char **argv)
{
char *valstr;
char *endptr = NULL;
bcm_xtlv_t *auth_tlv;
bcm_xtlv_t *val_tlv_ptr;
uint out_len;
uint32 temp;
uint16 count;
uint16 container_payload_len = 0;
int ret = 0;
char *p;
/* total output buffer will be an hc sub-category xtlv holding
* an xtvl of 1 or 2 uint32s.
*/
auth_tlv = (bcm_xtlv_t*)(&buf[0]);
auth_tlv->id = htol16(category);
val_tlv_ptr = (bcm_xtlv_t *)auth_tlv->data;
while (*argv) {
p = *argv++;
switch (p[1]) {
case 'a':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_AUTH_ENAB);
return BCME_USAGE_ERROR;
}
val_tlv_ptr->data[0] = (uint8)strtoul(valstr, &endptr, 0);
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_AUTH_ENAB);
val_tlv_ptr->len = htol16(sizeof(uint8));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint8),
BCM_XTLV_OPTION_NONE);
break;
case 'b':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_BLKLIST_AGE);
return BCME_USAGE_ERROR;
}
temp = htol32(strtoul(valstr, &endptr, 0));
memcpy(&val_tlv_ptr->data[0], &temp, sizeof(uint32));
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_BLKLIST_AGE);
val_tlv_ptr->len = htol16(sizeof(uint32));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint32),
BCM_XTLV_OPTION_NONE);
break;
case 'c':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_EAPOL_COUNT);
return BCME_USAGE_ERROR;
}
count = htol16(strtoul(valstr, &endptr, 0));
memcpy(&val_tlv_ptr->data[0], &count, sizeof(uint16));
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_EAPOL_COUNT);
val_tlv_ptr->len = htol16(sizeof(uint16));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint16),
BCM_XTLV_OPTION_NONE);
break;
case 'e':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_EAPOL_INTRVL);
return BCME_USAGE_ERROR;
}
temp = htol32(strtoul(valstr, &endptr, 0));
memcpy(&val_tlv_ptr->data[0], &temp, sizeof(uint32));
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_EAPOL_INTRVL);
val_tlv_ptr->len = htol16(sizeof(uint32));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint32),
BCM_XTLV_OPTION_NONE);
break;
case 'g':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_GTK_ROTATION);
return BCME_USAGE_ERROR;
}
temp = htol32(strtoul(valstr, &endptr, 0));
memcpy(&val_tlv_ptr->data[0], &temp, sizeof(uint32));
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_GTK_ROTATION);
val_tlv_ptr->len = htol16(sizeof(uint32));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint32),
BCM_XTLV_OPTION_NONE);
break;
case 'm':
valstr = *argv++;
if (!valstr || valstr[0] == '\0') {
printf("idauth config set: missing value for set of %d\n",
WL_IDAUTH_XTLV_BLKLIST_COUNT);
return BCME_USAGE_ERROR;
}
count = htol16(strtoul(valstr, &endptr, 0));
memcpy(&val_tlv_ptr->data[0], &count, sizeof(uint16));
val_tlv_ptr->id = htol16(WL_IDAUTH_XTLV_BLKLIST_COUNT);
val_tlv_ptr->len = htol16(sizeof(uint16));
container_payload_len += bcm_xtlv_size_for_data(sizeof(uint16),
BCM_XTLV_OPTION_NONE);
break;
default:
break;
}
val_tlv_ptr = (bcm_xtlv_t *)(&auth_tlv->data[container_payload_len]);
}
auth_tlv->len = htol16(container_payload_len);
out_len = bcm_xtlv_size_for_data(container_payload_len,
BCM_XTLV_OPTION_NONE);
ret = wlu_iovar_set(wl, "idauth", auth_tlv, out_len);
return ret;
}
static int
wl_idauth_config_get(void *wl, uint16 category, char **argv)
{
int ret = 0;
bcm_xtlv_t *auth_tlv;
bcm_xtlv_t *val_xtlv;
uint16 hc_len, hc_id;
uint16 val_len, val_id;
void *val_tlv_ptr;
uint16 len_of_tlvs;
const bcm_xtlv_opts_t no_pad = BCM_XTLV_OPTION_NONE;
if (*argv) {
printf("idauth config get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
/* init the wrapper hc category XTLV in the IO buffer and copy to it */
auth_tlv = (bcm_xtlv_t *)&buf[0];
auth_tlv->id = htol16(category);
auth_tlv->len = 0;
bcm_xtlv_pack_xtlv(auth_tlv, category, 0, 0, no_pad);
/* issue the 'hc' get with the one-item id list */
if ((ret = wlu_iovar_getbuf(wl, "idauth", buf, BCM_XTLV_HDR_SIZE, buf, WLC_IOCTL_SMLEN)))
return ret;
hc_len = ltoh16(auth_tlv->len);
hc_id = ltoh16(auth_tlv->id);
/* make sure the return xtlv is valid for the iobuffer */
if (!bcm_valid_xtlv(auth_tlv, WLC_IOCTL_SMLEN, no_pad)) {
printf("idauth config get:XTLV specifies length %u too long for iobuffer len %u\n",
hc_len, WLC_IOCTL_SMLEN);
return BCME_ERROR;
}
/* the return buf should start with the HC xtlv container */
if (hc_id != category) {
printf("idauth config get: return category %u did not match %u\n",
hc_id, category);
return BCME_ERROR;
}
/* make sure the container has enough room for an XTLV header */
if (hc_len < BCM_XTLV_HDR_SIZE) {
printf("idauth config get: return XTLV container specifies length %u too small"
"to hold any values\n",
hc_len);
return BCME_ERROR;
}
val_xtlv = (bcm_xtlv_t*)auth_tlv->data;
val_len = ltoh16(val_xtlv->len);
val_id = ltoh16(val_xtlv->id);
/* make sure the value xtlv is valid xtlv container */
if (!bcm_valid_xtlv(val_xtlv, hc_len, no_pad)) {
printf("idauth config get: return XTLV specifies length %u too long "
"for containing xtlv len %u\n",
val_len, hc_len);
return BCME_ERROR;
}
if (val_len == 0) {
printf("idauth config get: return value XTLV (id:%u) empty\n", val_id);
return BCME_ERROR;
}
/* print as words if multiple of word size, otherwise hexdump */
val_tlv_ptr = auth_tlv->data;
len_of_tlvs = auth_tlv->len;
while (len_of_tlvs && len_of_tlvs > BCM_XTLV_HDR_SIZE) {
val_xtlv = (bcm_xtlv_t *)val_tlv_ptr;
switch (val_xtlv->id) {
case WL_IDAUTH_XTLV_AUTH_ENAB:
printf("%s:%d\n", "auth_enab", *((uint8*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE +sizeof(uint8);
break;
case WL_IDAUTH_XTLV_GTK_ROTATION:
printf("%s:%d\n", "gtk_rot_interval", *((uint32*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE + sizeof(uint32);
break;
case WL_IDAUTH_XTLV_EAPOL_COUNT:
printf("%s:%d\n", "eapol_count", *((uint16*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE + sizeof(uint16);
break;
case WL_IDAUTH_XTLV_EAPOL_INTRVL:
printf("%s:%d\n", "eapol_interval", *((uint32*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE + sizeof(uint32);
break;
case WL_IDAUTH_XTLV_BLKLIST_COUNT:
printf("%s:%d\n", "mic_fail_cnt_blacklist",
*((uint16*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE + sizeof(uint16);
break;
case WL_IDAUTH_XTLV_BLKLIST_AGE:
printf("%s:%d\n", "blacklist_age", *((uint32*)val_xtlv->data));
len_of_tlvs -= BCM_XTLV_HDR_SIZE + sizeof(uint32);
break;
}
val_tlv_ptr = (uint8 *)val_tlv_ptr + bcm_xtlv_size((bcm_xtlv_t *)val_tlv_ptr,
BCM_XTLV_OPTION_NONE);
}
return (ret);
}
static int
wl_idauth_dump_counters(void *wl, uint16 category, char **argv)
{
int ret = 0;
bcm_xtlv_t *auth_tlv;
bcm_xtlv_t *val_xtlv;
uint16 hc_len, hc_id;
uint16 val_len, val_id;
uint32 *w;
const bcm_xtlv_opts_t no_pad = BCM_XTLV_OPTION_NONE;
if (*argv) {
printf("idauth counters get: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
/* init the wrapper hc category XTLV in the IO buffer and copy to it */
auth_tlv = (bcm_xtlv_t *)&buf[0];
auth_tlv->id = htol16(category);
auth_tlv->len = 0;
bcm_xtlv_pack_xtlv(auth_tlv, category, 0, 0, no_pad);
/* issue the 'hc' get with the one-item id list */
if ((ret = wlu_iovar_getbuf(wl, "idauth", buf, BCM_XTLV_HDR_SIZE, buf, WLC_IOCTL_SMLEN)))
return ret;
hc_len = ltoh16(auth_tlv->len);
hc_id = ltoh16(auth_tlv->id);
/* make sure the return xtlv is valid for the iobuffer */
if (!bcm_valid_xtlv(auth_tlv, WLC_IOCTL_SMLEN, no_pad)) {
printf("idauth counter get:XTLV specifies length %u too long for iobuffer len %u\n",
hc_len, WLC_IOCTL_SMLEN);
return BCME_ERROR;
}
/* the return buf should start with the HC xtlv container */
if (hc_id != category) {
printf("idauth counter get: return category %u did not match %u\n",
hc_id, category);
return BCME_ERROR;
}
/* make sure the container has enough room for an XTLV header */
if (hc_len < BCM_XTLV_HDR_SIZE) {
printf("idauth counter get: return XTLV container specifies length %u too small"
"to hold any values\n",
hc_len);
return BCME_ERROR;
}
val_xtlv = (bcm_xtlv_t*)auth_tlv->data;
val_len = ltoh16(val_xtlv->len);
val_id = ltoh16(val_xtlv->id);
/* make sure the value xtlv is valid xtlv container */
if (!bcm_valid_xtlv(val_xtlv, hc_len, no_pad)) {
printf("idauth counter: return XTLV specifies length %u too long "
"for containing xtlv len %u\n",
val_len, hc_len);
return BCME_ERROR;
}
if (val_len == 0) {
printf("idauth counter get: return value XTLV (id:%u) empty\n", val_id);
return BCME_ERROR;
}
/* print as words if multiple of word size, otherwise hexdump */
w = (uint32*)val_xtlv->data;
if (val_xtlv->len < sizeof(wl_idauth_counters_t))
printf("Auth counter length lesser than expected\n");
else
printf("%s %d\t %s %d\t%s %d\n", "authreq", w[0],
"micfail", w[1], "4wayhsfail", w[2]);
return (ret);
}
static int
wl_idauth_dump_peer_info(void *wl, uint16 category, char **argv)
{
int ret = 0;
bcm_xtlv_t *auth_tlv;
bcm_xtlv_t *val_xtlv;
uint16 hc_len, hc_id;
uint16 val_len, val_id;
auth_peer_t *w;
int idx;
const bcm_xtlv_opts_t no_pad = BCM_XTLV_OPTION_NONE;
if (*argv) {
printf("idauth peer_info: error, extra arg \"%s\"\n", *argv);
return BCME_USAGE_ERROR;
}
/* init the wrapper hc category XTLV in the IO buffer and copy to it */
auth_tlv = (bcm_xtlv_t *)&buf[0];
auth_tlv->id = htol16(category);
auth_tlv->len = 0;
bcm_xtlv_pack_xtlv(auth_tlv, category, 0, 0, no_pad);
/* issue the 'hc' get with the one-item id list */
if ((ret = wlu_iovar_getbuf(wl, "idauth", buf, BCM_XTLV_HDR_SIZE, buf, WLC_IOCTL_SMLEN)))
return ret;
hc_len = ltoh16(auth_tlv->len);
hc_id = ltoh16(auth_tlv->id);
/* make sure the return xtlv is valid for the iobuffer */
if (!bcm_valid_xtlv(auth_tlv, WLC_IOCTL_SMLEN, no_pad)) {
printf("idauth peer_info:XTLV specifies length %u too long for iobuffer len %u\n",
hc_len, WLC_IOCTL_SMLEN);
return BCME_ERROR;
}
/* the return buf should start with the HC xtlv container */
if (hc_id != category) {
printf("idauth peer_info: return category %u did not match %u\n",
hc_id, category);
return BCME_ERROR;
}
/* make sure the container has enough room for an XTLV header */
if (hc_len < BCM_XTLV_HDR_SIZE) {
if (hc_len == 0) {
printf("idauth peer_info: No peer present\n");
return ret;
} else {
printf("idauth peer_info: return XTLV container specifies"
"length %u too small"
"to hold any values\n",
hc_len);
return BCME_ERROR;
}
}
val_xtlv = (bcm_xtlv_t*)auth_tlv->data;
val_len = ltoh16(val_xtlv->len);
val_id = ltoh16(val_xtlv->id);
/* make sure the value xtlv is valid xtlv container */
if (!bcm_valid_xtlv(val_xtlv, hc_len, no_pad)) {
printf("idauth peer_info: return XTLV specifies length %u too long "
"for containing xtlv len %u\n",
val_len, hc_len);
return BCME_ERROR;
}
if (val_len == 0) {
printf("idauth peer_info: return value XTLV (id:%u) empty\n", val_id);
return BCME_OK;
}
/* print as words if multiple of word size, otherwise hexdump */
w = (auth_peer_t*)val_xtlv->data;
for (idx = 0; idx * sizeof(auth_peer_t) < val_xtlv->len; idx++) {
switch (w[idx].state) {
case WL_AUTH_PEER_STATE_AUTHORISED:
printf("STA %s\tState:%s\tBlacklist Age:%d\n",
wl_ether_etoa(&w[idx].peer_addr), "AUTHORIZED", 0);
break;
case WL_AUTH_PEER_STATE_BLACKLISTED:
printf("STA %s\tState:%s\tBlacklist Age:%d\n",
wl_ether_etoa(&w[idx].peer_addr), "BLACKLISTED",
w[idx].blklist_end_time);
break;
case WL_AUTH_PEER_STATE_4WAY_HS_ONGOING:
printf("STA %s\tState:%s\tBlacklist Age:%d\n",
wl_ether_etoa(&w[idx].peer_addr), "4WAY_HS_ONGOING", 0);
break;
default:
printf("idauth peer info: unknown state\n");
}
}
return (ret);
}
static int
fsize(void *fp)
{
long size = BCME_ERROR;
long cur_offset = BCME_ERROR;
if ((cur_offset = ftell(fp)) < 0) {
fprintf(stderr, "Could not determine current file offset : %s\n", strerror(errno));
return BCME_ERROR;
}
if (!fseek(fp, 0, SEEK_END)) {
if ((size = ftell(fp)) < 0)
fprintf(stderr, "Could not determine file size : %s\n", strerror(errno));
fseek(fp, cur_offset, SEEK_SET);
}
else
fprintf(stderr, "Error in performing fseek: %s\n", strerror(errno));
return (int)size;
}
static int
wl_wake_timer(void *wl, cmd_t *cmd, char **argv)
{
int err, buflen;
wake_timer_t *w_timer;
char *endptr;
buflen = sprintf(buf, "%s", *argv) + 1;
if (*++(argv) == NULL) {
buf[buflen] = '\0';
err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_SMLEN);
if (err < 0)
return err;
w_timer = (wake_timer_t *)buf;
if (dtoh16(w_timer->ver) != WAKE_TIMER_VERSION) {
err = BCME_VERSION;
return err;
}
printf("Status:%s\n", dtoh16(w_timer->limit) ? "ENABLED": "DISABLED");
printf("Num of events delivered:%d\n", dtoh16(w_timer->count));
return 0;
} else {
w_timer = (wake_timer_t *) (buf + buflen);
buflen += sizeof(wake_timer_t);
w_timer->period = htod16(strtoul(*argv, &endptr, 0));
if (*endptr != '\0') {
fprintf(stderr, "Type '%s' not a number?\n", *argv);
return BCME_ERROR;
}
if (*++(argv) == NULL) {
printf("Missing arg <limit>\n");
goto usage;
}
w_timer->limit = htod16(strtoul(*argv, &endptr, 0));
if (*endptr != '\0') {
fprintf(stderr, "Type '%s' not a number?\n", *argv);
return BCME_ERROR;
}
if (*++(argv)) {
printf("extra arguments\n");
goto usage;
}
w_timer->ver = htod16(WAKE_TIMER_VERSION);
w_timer->len = htod16(sizeof(wake_timer_t));
err = wlu_set(wl, WLC_SET_VAR, buf, buflen);
return err;
}
usage:
printf("Usage: wl wake_timer <period> <limit>\n");
return 0;
}
static int
wl_utrace_capture(void *wl, cmd_t *cmd, char **argv)
{
void *buff = NULL;
uint16 *ptr = NULL;
uint j;
FILE *fptr = NULL;
uint32 capture_args_size = 0;
uchar rev_2 = FALSE;
int err = BCME_ERROR;
uint32 flag = WLC_UTRACE_MORE_DATA;
UNUSED_PARAMETER(cmd);
UNUSED_PARAMETER(argv);
/* Use _v1 version of structure for DINGO and other legacy chips and _v2 for newer chips. */
if (wlc_ver_major(wl) <= 5) {
/* Handle Legacy chips and chips from DINGO2 */
capture_args_size = sizeof(wl_utrace_capture_args_v1_t);
rev_2 = FALSE;
} else {
capture_args_size = sizeof(wl_utrace_capture_args_v2_t);
rev_2 = TRUE;
}
fptr = fopen("utrace.txt", "w");
if (fptr == NULL) {
return BCME_NORESOURCE;
}
buff = malloc(WLC_UTRACE_LEN + capture_args_size);
if (buff == NULL) {
fclose(fptr);
return BCME_NOMEM;
}
memset(buff, 0, WLC_UTRACE_LEN + capture_args_size);
do {
if ((err = wlu_iovar_getbuf(wl, cmd->name,
buff, 0, buff, (WLC_UTRACE_LEN + capture_args_size))) < 0)
{
goto exit;
}
if (rev_2 == TRUE) {
wl_utrace_capture_args_v2_t *capture_args;
capture_args = (wl_utrace_capture_args_v2_t *) buff;
flag = capture_args->flag;
/* Check the structure rev */
if (capture_args->version == UTRACE_CAPTURE_VER_2) {
capture_args->length -= capture_args_size;
ptr = (uint16 *) ((uchar*)buff + capture_args_size);
for (j = 0; j < capture_args->length/sizeof(uint32); j++) {
fprintf(fptr, "%04x %04x \n", ptr[j*2 + 1], ptr[j*2]);
}
} else {
err = BCME_VERSION;
flag = WLC_UTRACE_READ_END;
printf("Error:Version Mismatch App:%d FW:%d\n",
UTRACE_CAPTURE_VER_2, capture_args->version);
break;
}
} else {
wl_utrace_capture_args_v1_t *capture_args;
capture_args = (wl_utrace_capture_args_v1_t *) buff;
flag = capture_args->flag;
ptr = (uint16 *) ((uchar*)buff + capture_args_size);
for (j = 0; j < capture_args->length/sizeof(uint32); j++) {
fprintf(fptr, "%04x %04x \n", ptr[j*2 + 1], ptr[j*2]);
}
}
} while (flag == WLC_UTRACE_MORE_DATA);
exit:
free(buff);
fclose(fptr);
return err;
}
static int
wl_wds_ap_ifname(void *wl, cmd_t *cmd, char **argv)
{
int ret;
UNUSED_PARAMETER(argv);
memset(buf, 0, WLC_IOCTL_SMLEN);
/* query for 'wds_ap_ifname' to get ap ifname */
ret = wlu_iovar_get(wl, cmd->name, buf, WLC_IOCTL_SMLEN);
buf[WLC_IOCTL_SMLEN -1] = '\0';
/* if the query is successful, continue on and print the result. */
if (ret) {
return ret;
}
printf("%s\n", buf);
return ret;
}
static int
wl_netx_ifconfig(void *wl, cmd_t *cmd, char **argv)
{
int ret;
struct ipv4_addr ipa_set;
netx_ifconfig_t ifconfig;
uint8 *bufptr;
int i;
int argc;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
if (argc == 4) {
/* Add host addresses, a list of ip, netmask, gateway */
bufptr = (uint8*)&ifconfig;
for (i = 1; i < argc; i++)
{
if (!wl_atoip(argv[i], &ipa_set))
return BCME_USAGE_ERROR;
memcpy(bufptr, &ipa_set.addr[0], IPV4_ADDR_LEN);
bufptr += IPV4_ADDR_LEN;
}
return wlu_var_setbuf(wl, cmd->name, &ifconfig, sizeof(ifconfig));
}
else {
void *ptr = NULL;
netx_ifconfig_t *if_get;
if ((ret = wlu_var_getbuf(wl, cmd->name, &ifconfig, sizeof(ifconfig), &ptr)) < 0)
return ret;
if_get = (netx_ifconfig_t *)ptr;
printf("ip: %s ", wl_iptoa((struct ipv4_addr *)&if_get->ipaddr));
printf("netmask: %s ", wl_iptoa((struct ipv4_addr *)&if_get->ipaddr_netmask));
printf("gw: %s\n", wl_iptoa((struct ipv4_addr *)&if_get->ipaddr_gateway));
}
return 0;
}
#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 */