| /* |
| * wl pfn command module |
| * |
| * 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: wluc_pfn.c 458728 2014-02-27 18:15:25Z $ |
| */ |
| |
| #ifdef WIN32 |
| #include <windows.h> |
| #endif |
| |
| #include <wlioctl.h> |
| |
| #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 <bcmutils.h> |
| #include <bcmendian.h> |
| #include "wlu_common.h" |
| #include "wlu.h" |
| |
| #ifdef WIN32 |
| #define bzero(b, len) memset((b), 0, (len)) |
| #endif |
| |
| #ifdef LINUX |
| #include <unistd.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <sys/socket.h> |
| #include <sys/time.h> |
| #include <sys/ioctl.h> |
| #include <netinet/in.h> |
| #include <net/if.h> |
| #include <linux/if_packet.h> |
| #endif /* LINUX */ |
| |
| static cmd_func_t wl_pfn_set; |
| static cmd_func_t wl_pfn_add; |
| static cmd_func_t wl_pfn_ssid_param; |
| static cmd_func_t wl_pfn_add_bssid; |
| static cmd_func_t wl_pfn_cfg; |
| static cmd_func_t wl_pfn; |
| static cmd_func_t wl_pfnbest, wl_pfnbest_bssid; |
| static cmd_func_t wl_pfn_suspend; |
| static cmd_func_t wl_pfnlbest; |
| static cmd_func_t wl_pfn_mem; |
| static cmd_func_t wl_pfn_override; |
| static cmd_func_t wl_pfn_macaddr; |
| #if defined(linux) |
| static cmd_func_t wl_pfn_event_check; |
| #endif /* linux */ |
| static cmd_func_t wl_event_filter; |
| static cmd_func_t wl_pfn_roam_alert_thresh; |
| #ifdef WL_MPF |
| static cmd_func_t wl_pfn_mpfset; |
| static cmd_func_t wl_mpf_map; |
| static cmd_func_t wl_mpf_state; |
| #endif /* WL_MPF */ |
| |
| static cmd_t wl_pfn_cmds[] = { |
| { "pfnset", wl_pfn_set, -1, -1, |
| "Configures preferred network offload parameter\n" |
| "\tpfnset syntax is: pfnset [scanfrq xxxxx(30 sec)] [netimeout xxxx(60 sec)]" |
| "[slowfrq xxxx(180 sec)] [bestn (2)|[1-BESTN_MAX]] [mscan (0)|[0-MSCAN_MAX]]" |
| "[bdscan (0)|1] [adapt (off)|[smart, strict, slow]]" |
| "[rssi_delta xxxx(30 dBm)] [sort (listorder)|rssi] [bkgscan (0)|1] [immediateevent (0)|1]" |
| "[immediate 0|(1)] [repeat (10)|[1-20]] [exp (2)|[1-5]] [separate 0|(1)]" |
| "[bestn_bssid (0)|1]"}, |
| { "pfnadd", wl_pfn_add, -1, -1, |
| "Adding SSID based preferred networks to monitor and connect\n" |
| "\tpfnadd syntax is: pfnadd ssid <SSID> [hidden (0)|1]" |
| "[imode (bss)|ibss] [same_network (0)|1] [imprecise (0)|1] [trig a|abg|bg]" |
| "[clear] [no_aging (0)|1]" |
| "[amode (open)|shared] [wpa_auth (wpadisabled)|wpapsk|wpa2psk|wpanone|any]" |
| "[wsec WEP|TKIP|AES|TKIPAES] [suppress (neither)|found|lost] [rssi <rssi>(0 dBm)]\n" |
| "Up to 16 SSID networks can be added together in one pfnadd\n" |
| "\tTo specify more than one WPA methods, use a number (same format as wpa_auth iovar) " |
| "as the parameter of wpa_auth (e.g. 0x84 for wpapsk and wpa2psk.)"}, |
| { "pfn_ssid_cfg", wl_pfn_ssid_param, WLC_GET_VAR, WLC_SET_VAR, |
| "Adding PFN SSID params to be used to determine FOUND when associated\n" |
| "\tpfn_ssid_cfg syntax is: pfn_ssid_cfg [min5g_rssi <rssi>(-45 dBm)]\n" |
| "\t[min2g_rssi <rssi>(-50 dBm)] [init_score_max (110)]\n" |
| "\t[cur_bssid_bonus (0)] [same_ssid_bonus (0)] [secure_bonus (0)]\n" |
| "\t[band_5g_bonus (0)] [clear]"}, |
| { "pfnadd_bssid", wl_pfn_add_bssid, -1, -1, |
| "Adding BSSID based preferred networks to monitor and connect\n" |
| "\tpfnadd_bssid syntax is: pfnadd_bssid bssid <BSSID> [suppress (neither)|found|lost]" |
| "[rssi <rssi>(0 dBm)]\n" |
| "\tUp to 150 BSSIDs can be added together in one pfnadd_bssid"}, |
| { "pfncfg", wl_pfn_cfg, -1, -1, |
| "Configures channel list and report type\n" |
| "Usage: pfncfg [channel <list>] [report <type>] [prohibited 1|0] [history_off 1|0]\n" |
| "\treport <type> is ssidonly, bssidonly, or both (default: both)\n" |
| "\tprohibited flag 1: allow and (passively) scan any channel (default 0)"}, |
| { "pfn", wl_pfn, -1, -1, |
| "Enable/disable preferred network off load monitoring\n" |
| "\tpfn syntax is: pfn 0|1"}, |
| { "pfnclear", wl_var_void, -1, WLC_SET_VAR, |
| "Clear the preferred network list\n" |
| "\tpfnclear syntax is: pfnclear"}, |
| { "pfnbest", wl_pfnbest, -1, -1, |
| "Get the best n networks in each of up to m scans, with 16bit timestamp\n" |
| "\tpfnbest syntax is: pfnbest"}, |
| { "pfnlbest", wl_pfnlbest, -1, -1, |
| "Get the best n networks in each scan, up to m scans, with 32bit timestamp\n" |
| "\tpfnbest syntax is: pfnlbest"}, |
| { "pfnbest_bssid", wl_pfnbest_bssid, -1, -1, |
| "Get the best n networks in each of up to m scans, without ssid\n" |
| "\tpfnbest syntax is: pfnbest_bssid"}, |
| { "pfnsuspend", wl_pfn_suspend, -1, -1, |
| "Suspend/resume pno scan\n" |
| "\tpfnsuspend syntax is: pfnsuspend 0|1"}, |
| { "pfnmem", wl_pfn_mem, -1, -1, |
| "Get supported mscan with given bestn\n" |
| "\tpfnmem syntax is: pfnmscan bestn [1-BESTN_MAX]"}, |
| #if defined(linux) |
| { "pfneventchk", wl_pfn_event_check, -1, -1, |
| "Listen and prints the preferred network off load event from dongle\n" |
| "\tpfneventchk syntax is: pfneventchk [ifname]"}, |
| #endif /* linux */ |
| { "event_filter", wl_event_filter, -1, -1, |
| "Set/get event filter\n" |
| "\tevent_filter syntax is: event_filter [value]"}, |
| { "pfn_roam_alert_thresh", wl_pfn_roam_alert_thresh, WLC_GET_VAR, WLC_SET_VAR, |
| "Get/Set PFN and roam alert threshold\n" |
| "\tUsage: wl pfn_roam_alert_thresh [pfn_alert_thresh] [roam_alert_thresh]" |
| }, |
| { "pfn_override", wl_pfn_override, WLC_GET_VAR, WLC_SET_VAR, |
| "Temporary override for PNO scan parameters:\n" |
| " pfn_override [<start> <duration> scanfrq <secs> [netimeout <secs>]\n" |
| " [adapt (off | smart | strict | slow)] [repeat cnt>]\n" |
| " [exp <cnt>] [slowfrq <secs>]]\n" |
| " <start> is seconds until these parameters should be activated\n" |
| " <duration> is seconds these parameters should remain in force\n" |
| " Unspecified parameters use the values from pfnset."}, |
| { "pfn_macaddr", wl_pfn_macaddr, WLC_GET_VAR, WLC_SET_VAR, |
| "Private MAC address to use as source for PNO scans:\n" |
| " pfn_macaddr [<mac>]"}, |
| #ifdef WL_MPF |
| { "pfn_mpfset", wl_pfn_mpfset, WLC_GET_VAR, WLC_SET_VAR, |
| "Get/Set mpf-based pfn parameters\n" |
| " pfn_mpfset <groupid> [state <state> scanfrq <secs> [netimeout <secs>]\n" |
| " [adapt (off | smart | strict | slow)] [repeat <cnt>]\n" |
| " [exp <cnt>] [slowfrq <secs>] ] ...\n" |
| " <groupid> is 0 (SSID list) or 1 (BSSID list)\n" |
| " <state> is 0 thru 3\n" |
| " unspecified states or subparameters us the values from pfnset."}, |
| { "mpf_map", wl_mpf_map, WLC_GET_VAR, WLC_SET_VAR, |
| " mpf_map [type <type>] <mask> <val>/<state>[/<name>] ...\n" |
| " <type> must be 0 if present no (effect)\n" |
| " <mask> and <val> are 16-bit hex values, max 1s allowed: 3\n" |
| " <state> is a small number, 0 thru N-1 (N is # of bit combos)\n" |
| " <name> is an optional string name for the state"}, |
| { "mpf_state", wl_mpf_state, WLC_GET_VAR, WLC_SET_VAR, |
| " mpf_state [type <type>] [<state> | <name> | gpio ]\n" |
| " <mpf_state [type <type>] [<state> | <name> | gpio ]\n" |
| " <type> must be 0 if present no (effect)\n" |
| " <state> or <name>, if specified, override current value,\n" |
| " setting gpio returns to simply tracking hardware state"}, |
| #endif /* WL_MPF */ |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_pfn_module_init(void) |
| { |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register pfn commands */ |
| wl_module_cmds_register(wl_pfn_cmds); |
| } |
| |
| static int |
| wl_pfn_set(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_param_t pfn_param; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| /* Setup default values */ |
| pfn_param.version = PFN_VERSION; |
| /* Sorting based on list order, no back ground scan, no autoswitch, |
| * no immediate event report, no adaptvie scan, but immediate scan |
| */ |
| pfn_param.flags = (PFN_LIST_ORDER << SORT_CRITERIA_BIT | ENABLE << IMMEDIATE_SCAN_BIT); |
| /* Scan frequency of 30 sec */ |
| pfn_param.scan_freq = 30; |
| /* slow adapt scan is off by default */ |
| pfn_param.slow_freq = 0; |
| /* RSSI margin of 30 dBm */ |
| pfn_param.rssi_margin = 30; |
| /* Network timeout 60 sec */ |
| pfn_param.lost_network_timeout = 60; |
| /* best n = 2 by default */ |
| pfn_param.bestn = DEFAULT_BESTN; |
| /* mscan m=0 by default, so not record best networks by default */ |
| pfn_param.mscan = DEFAULT_MSCAN; |
| /* default repeat = 10 */ |
| pfn_param.repeat = DEFAULT_REPEAT; |
| /* by default, maximum scan interval = 2^2*scan_freq when adaptive scan is turned on */ |
| pfn_param.exp = DEFAULT_EXP; |
| |
| while (*++argv) { |
| if (!stricmp(*argv, "scanfrq")) { |
| if (*++argv) |
| pfn_param.scan_freq = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing scanfrq option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "netimeout")) { |
| if (*++argv) |
| pfn_param.lost_network_timeout = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing netimeout option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "rssi_delta")) { |
| if (*++argv) |
| pfn_param.rssi_margin = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing rssi_delta option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "sort")) { |
| if (*++argv) { |
| pfn_param.flags &= ~SORT_CRITERIA_MASK; |
| if (!stricmp(*argv, "listorder")) |
| pfn_param.flags |= (PFN_LIST_ORDER << SORT_CRITERIA_BIT); |
| else if (!stricmp(*argv, "rssi")) |
| pfn_param.flags |= (PFN_RSSI << SORT_CRITERIA_BIT); |
| else { |
| fprintf(stderr, "Invalid sort option %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "Missing sort option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "immediateevent")) { |
| if (*++argv) { |
| if (!stricmp(*argv, "1")) { |
| pfn_param.flags |= IMMEDIATE_EVENT_MASK; |
| } else if (!stricmp(*argv, "0")) { |
| pfn_param.flags &= ~IMMEDIATE_EVENT_MASK; |
| } else { |
| fprintf(stderr, "Invalid immediateevent option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "Missing immediateevent option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "bkgscan")) { |
| if (*++argv) { |
| pfn_param.flags &= ~ENABLE_BKGRD_SCAN_MASK; |
| if (atoi(*argv)) |
| pfn_param.flags |= (ENABLE << ENABLE_BKGRD_SCAN_BIT); |
| else |
| pfn_param.flags |= (DISABLE << ENABLE_BKGRD_SCAN_BIT); |
| } else { |
| fprintf(stderr, "Missing bkgscan option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "immediate")) { |
| pfn_param.flags &= ~IMMEDIATE_SCAN_MASK; |
| if (*++argv) { |
| if (atoi(*argv)) |
| pfn_param.flags |= (ENABLE << IMMEDIATE_SCAN_BIT); |
| else |
| pfn_param.flags |= (DISABLE << IMMEDIATE_SCAN_BIT); |
| } else { |
| fprintf(stderr, "Missing immediate option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "bdscan")) { |
| if (*++argv) { |
| pfn_param.flags &= ~ENABLE_BD_SCAN_MASK; |
| if (atoi(*argv)) |
| pfn_param.flags |= (ENABLE << ENABLE_BD_SCAN_BIT); |
| else |
| pfn_param.flags |= (DISABLE << ENABLE_BD_SCAN_BIT); |
| } else { |
| fprintf(stderr, "Missing bdscan option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "separate")) { |
| if (*++argv) { |
| pfn_param.flags &= ~REPORT_SEPERATELY_MASK; |
| if (atoi(*argv)) |
| pfn_param.flags |= (ENABLE << REPORT_SEPERATELY_BIT); |
| else |
| pfn_param.flags |= (DISABLE << REPORT_SEPERATELY_BIT); |
| } else { |
| fprintf(stderr, "Missing seperate option\n"); |
| return -1; |
| } |
| } else if (!stricmp(*argv, "adapt")) { |
| if (*++argv) { |
| pfn_param.flags &= ~ENABLE_ADAPTSCAN_MASK; |
| if (!stricmp(*argv, "off")) { |
| pfn_param.flags |= (OFF_ADAPT << ENABLE_ADAPTSCAN_BIT); |
| } else if (!stricmp(*argv, "smart")) { |
| pfn_param.flags |= (SMART_ADAPT << ENABLE_ADAPTSCAN_BIT); |
| } else if (!stricmp(*argv, "strict")) { |
| pfn_param.flags |= (STRICT_ADAPT << ENABLE_ADAPTSCAN_BIT); |
| } else if (!stricmp(*argv, "slow")) { |
| pfn_param.flags |= (SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT); |
| } else { |
| fprintf(stderr, "Invalid adaptive scan option %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "Missing adaptive scan option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "bestn")) { |
| pfn_param.bestn = atoi(*++argv); |
| } else if (!stricmp(*argv, "mscan")) { |
| pfn_param.mscan = atoi(*++argv); |
| } else if (!stricmp(*argv, "repeat")) { |
| pfn_param.repeat = atoi(*++argv); |
| if (pfn_param.repeat < 1 || pfn_param.repeat > 20) { |
| fprintf(stderr, "repeat %d out of range (1-20)\n", |
| pfn_param.repeat); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "exp")) { |
| pfn_param.exp = atoi(*++argv); |
| if (pfn_param.exp < 1 || pfn_param.exp > 5) { |
| fprintf(stderr, "exp %d out of range (1-5)\n", |
| pfn_param.exp); |
| return BCME_BADARG; |
| } |
| } else if (!stricmp(*argv, "slowfrq")) { |
| if (*++argv) |
| pfn_param.slow_freq = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing slowfrq option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "bestn_bssid")) { |
| if (*++argv) { |
| pfn_param.flags &= ~BESTN_BSSID_ONLY_MASK; |
| if (atoi(*argv)) |
| pfn_param.flags |= (ENABLE << BESTN_BSSID_ONLY_BIT); |
| else |
| pfn_param.flags |= (DISABLE << BESTN_BSSID_ONLY_BIT); |
| } else { |
| fprintf(stderr, "Missing bestn_bssid option\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| } |
| |
| if ((((pfn_param.flags & ENABLE_ADAPTSCAN_MASK) == |
| (SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT)) && |
| !pfn_param.slow_freq) || |
| (((pfn_param.flags & ENABLE_ADAPTSCAN_MASK) != |
| (SLOW_ADAPT << ENABLE_ADAPTSCAN_BIT)) && |
| pfn_param.slow_freq)) { |
| fprintf(stderr, "SLOW_ADAPT flag and slowfrq value not match\n"); |
| return BCME_BADARG; |
| } |
| pfn_param.version = htod32(pfn_param.version); |
| pfn_param.scan_freq = htod32(pfn_param.scan_freq); |
| pfn_param.lost_network_timeout = htod32(pfn_param.lost_network_timeout); |
| pfn_param.flags = htod16(pfn_param.flags); |
| pfn_param.rssi_margin = htod16(pfn_param.rssi_margin); |
| pfn_param.slow_freq = htod32(pfn_param.slow_freq); |
| |
| if ((err = wlu_iovar_set(wl, "pfn_set", &pfn_param, sizeof(wl_pfn_param_t)))) |
| return (err); |
| |
| return (0); |
| } |
| |
| static bool |
| validate_hex(char hexchar) |
| { |
| if ((hexchar >= '0' && hexchar <= '9') || |
| (hexchar >= 'a' || hexchar <= 'z') || |
| (hexchar >= 'A' || hexchar <= 'Z')) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| |
| static uint8 |
| char2hex(char hexchar) |
| { |
| if (hexchar >= '0' && hexchar <= '9') |
| return (hexchar - '0'); |
| else if (hexchar >= 'a' && hexchar <= 'f') |
| return (hexchar - 'a' + 10); |
| else if (hexchar >= 'A' && hexchar <= 'F') |
| return (hexchar - 'A' + 10); |
| else |
| { |
| fprintf(stderr, "non-hex\n"); |
| return 0xff; |
| } |
| } |
| |
| #define MAXNUM_SSID_PER_ADD 16 |
| |
| static int |
| wl_pfn_add(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_t *p_pfn_element = NULL; |
| int i, pfn_element_len, cnt; |
| wl_pfn_t *pssidnet = NULL; |
| uint32 val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| pfn_element_len = MAXNUM_SSID_PER_ADD * sizeof(wl_pfn_t); |
| p_pfn_element = (wl_pfn_t *)malloc(pfn_element_len); |
| if (p_pfn_element == NULL) { |
| fprintf(stderr, "Failed to allocate buffer for %d bytes\n", pfn_element_len); |
| return BCME_NOMEM; |
| } |
| memset(p_pfn_element, '\0', pfn_element_len); |
| |
| pssidnet = p_pfn_element; |
| for (i = 0; i < MAXNUM_SSID_PER_ADD; i++) { |
| /* Default setting, open, no WPA, no WEP and bss */ |
| pssidnet->auth = DOT11_OPEN_SYSTEM; |
| pssidnet->wpa_auth = WPA_AUTH_DISABLED; |
| pssidnet->wsec = 0; |
| pssidnet->infra = 1; |
| pssidnet->flags = 0; |
| pssidnet++; |
| } |
| cnt = -1; |
| pssidnet = p_pfn_element; |
| while (*++argv) { |
| if (!stricmp(*argv, "ssid")) { |
| if (*++argv) { |
| if (++cnt >= MAXNUM_SSID_PER_ADD) { |
| fprintf(stderr, "exceed max 16 SSID per pfn_add\n"); |
| err = BCME_BADARG; |
| goto error; |
| } |
| if (cnt > 0) { |
| pssidnet->flags = htod32(pssidnet->flags); |
| pssidnet++; |
| } |
| strncpy((char *)pssidnet->ssid.SSID, *argv, |
| sizeof(pssidnet->ssid.SSID)); |
| pssidnet->ssid.SSID_len = |
| strlen((char *)pssidnet->ssid.SSID); |
| if (pssidnet->ssid.SSID_len > 32) { |
| fprintf(stderr, "SSID too long: %s\n", *argv); |
| err = BCME_BADARG; |
| goto error; |
| } |
| pssidnet->ssid.SSID_len = htod32(pssidnet->ssid.SSID_len); |
| } else { |
| fprintf(stderr, "no value for ssid\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } |
| else if (!stricmp(*argv, "hidden")) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (*++argv) { |
| val = **argv - '0'; |
| if (val != ENABLE && val != DISABLE) { |
| fprintf(stderr, "invalid hidden setting, use 0/1\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| pssidnet->flags |= val << WL_PFN_HIDDEN_BIT; |
| } else { |
| fprintf(stderr, "no value for hidden\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "imode")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (!stricmp(*argv, "bss")) { |
| pssidnet->infra = 1; |
| } else if (!stricmp(*argv, "ibss")) { |
| pssidnet->infra = 0; |
| } else { |
| fprintf(stderr, "Invalid imode arg %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| pssidnet->infra = htod32(pssidnet->infra); |
| } else { |
| fprintf(stderr, "Missing option for imode\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "amode")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (!stricmp(*argv, "open")) |
| pssidnet->auth = DOT11_OPEN_SYSTEM; |
| else if (!stricmp(*argv, "shared")) |
| pssidnet->auth = DOT11_SHARED_KEY; |
| else { |
| fprintf(stderr, "Invalid imode arg %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| pssidnet->auth = htod32(pssidnet->auth); |
| } else { |
| fprintf(stderr, "Missing option for amode\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "wpa_auth")) { |
| if (*++argv) { |
| uint32 wpa_auth; |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| |
| /* figure requested auth, allow "any" */ |
| if (!stricmp(*argv, "wpapsk")) |
| pssidnet->wpa_auth = WPA_AUTH_PSK; |
| else if (!stricmp(*argv, "wpa2psk")) |
| pssidnet->wpa_auth = WPA2_AUTH_PSK; |
| else if (!stricmp(*argv, "wpadisabled")) |
| pssidnet->wpa_auth = WPA_AUTH_DISABLED; |
| else if (!stricmp(*argv, "any")) |
| pssidnet->wpa_auth = (uint16)WPA_AUTH_PFN_ANY; |
| else if ((wpa_auth = strtoul(*argv, 0, 0))) |
| pssidnet->wpa_auth = wpa_auth; |
| else { |
| fprintf(stderr, "Invalid wpa_auth option %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| pssidnet->wpa_auth = htod32(pssidnet->wpa_auth); |
| } else { |
| fprintf(stderr, "Missing option for wpa_auth\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "wsec")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (!stricmp(*argv, "WEP")) { |
| pssidnet->wsec = WEP_ENABLED; |
| } else if (!stricmp(*argv, "TKIP")) |
| pssidnet->wsec = TKIP_ENABLED; |
| else if (!stricmp(*argv, "AES")) |
| pssidnet->wsec = AES_ENABLED; |
| else if (!stricmp(*argv, "TKIPAES")) |
| pssidnet->wsec = TKIP_ENABLED | AES_ENABLED; |
| else { |
| fprintf(stderr, "Invalid wsec option %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| pssidnet->wsec = htod32(pssidnet->wsec); |
| } else { |
| fprintf(stderr, "Missing option for wsec\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "suppress")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (!stricmp(*argv, "found")) { |
| pssidnet->flags |= WL_PFN_SUPPRESSFOUND_MASK; |
| } else if (!stricmp(*argv, "lost")) { |
| pssidnet->flags |= WL_PFN_SUPPRESSLOST_MASK; |
| } else if (!stricmp(*argv, "neither")) { |
| pssidnet->flags &= |
| ~(WL_PFN_SUPPRESSLOST_MASK | WL_PFN_SUPPRESSFOUND_MASK); |
| } else { |
| fprintf(stderr, "Invalid suppress option %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for suppress\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "rssi")) { |
| if (*++argv) { |
| int rssi = atoi(*argv); |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (rssi >= -128 && rssi <= 0) { |
| pssidnet->flags |= (rssi << WL_PFN_RSSI_SHIFT) |
| & WL_PFN_RSSI_MASK; |
| } else { |
| fprintf(stderr, "Invalid rssi option %s\n", *argv); |
| err = BCME_BADARG; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for rssi\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "trig")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (!stricmp(*argv, "a")) { |
| pssidnet->flags |= WL_PFN_SSID_A_BAND_TRIG; |
| } else if (!stricmp(*argv, "bg")) { |
| pssidnet->flags |= WL_PFN_SSID_BG_BAND_TRIG; |
| } else if (!stricmp(*argv, "abg")) { |
| pssidnet->flags |= |
| (WL_PFN_SSID_A_BAND_TRIG | WL_PFN_SSID_BG_BAND_TRIG); |
| } else { |
| fprintf(stderr, "Invalid trigger option %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for trigger\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "same_network")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| val = **argv - '0'; |
| if (val != ENABLE && val != DISABLE) { |
| fprintf(stderr, "invalid same_network setting, use 0/1\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (val) { |
| pssidnet->flags |= WL_PFN_SSID_SAME_NETWORK; |
| } |
| } else { |
| fprintf(stderr, "Missing option for same_network\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "no_aging")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| val = **argv - '0'; |
| if (val != ENABLE && val != DISABLE) { |
| fprintf(stderr, "invalid same_network setting, use 0/1\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (val) { |
| pssidnet->flags |= WL_PFN_SUPPRESS_AGING_MASK; |
| } |
| } else { |
| fprintf(stderr, "Missing option for same_network\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "imprecise")) { |
| if (*++argv) { |
| if (pssidnet->ssid.SSID_len == 0) { |
| fprintf(stderr, "Wrong! Start with SSID\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| val = **argv - '0'; |
| if (val != ENABLE && val != DISABLE) { |
| fprintf(stderr, "invalid imprecise setting, use 0/1\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| if (val) { |
| pssidnet->flags |= WL_PFN_SSID_IMPRECISE_MATCH; |
| } |
| } else { |
| fprintf(stderr, "Missing option for imprecise match\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "clear")) { |
| if (*++argv) { |
| if (cnt >= 1) { |
| fprintf(stderr, "Will only clear, ssids ignored\n"); |
| } |
| cnt = 0; |
| pssidnet = p_pfn_element; |
| pssidnet->flags = WL_PFN_FLUSH_ALL_SSIDS; |
| } else { |
| fprintf(stderr, "Missing option for clear\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } |
| pssidnet->flags = htod32(pssidnet->flags); |
| |
| pfn_element_len = (cnt + 1) * sizeof(wl_pfn_t); |
| if ((err = wlu_iovar_set(wl, "pfn_add", p_pfn_element, |
| pfn_element_len))) { |
| fprintf(stderr, "pfn_add fail\n"); |
| goto error; |
| } |
| free(p_pfn_element); |
| return (0); |
| |
| error: |
| free(p_pfn_element); |
| return err; |
| } |
| |
| #define DEFAULT_INIT_SCORE_MAX 110 |
| #define DEFAULT_MIN_5G_RSSI -45 |
| #define DEFAULT_MIN_2G_RSSI -50 |
| |
| static int |
| wl_pfn_ssid_param(void *wl, cmd_t *cmd, char **argv) |
| { |
| int i = 0, err = BCME_OK; |
| wl_pfn_ssid_cfg_t cfg; |
| char *endptr = NULL; |
| uint16 val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| /* process a GET */ |
| if (!*(argv + 1)) { |
| if (cmd->get < 0) |
| return -1; |
| memset(&cfg, 0, sizeof(cfg)); |
| if ((err = wlu_iovar_get(wl, "pfn_ssid_cfg", (void *)&cfg, |
| sizeof(wl_pfn_ssid_cfg_t))) < 0) { |
| fprintf(stderr, "Failed to get pfn_ssid_cfg %d\n", err); |
| return err; |
| } |
| if (cfg.version != WL_PFN_SSID_CFG_VERSION) { |
| fprintf(stderr, "Mismatched version expected %d, got %d\n", |
| WL_PFN_SSID_CFG_VERSION, cfg.version); |
| return err; |
| } |
| fprintf(stderr, "min2G_rssi %d min5G_rssi %d init_score_max %d\n", |
| cfg.params.min2G_rssi, cfg.params.min5G_rssi, |
| dtoh16(cfg.params.init_score_max)); |
| fprintf(stderr, "band_5g_bonus %d secure_bonus %d same_ssid_bonus %d\n", |
| dtoh16(cfg.params.band_5g_bonus), dtoh16(cfg.params.secure_bonus), |
| dtoh16(cfg.params.same_ssid_bonus)); |
| fprintf(stderr, "cur_bssid_bonus %d\n", dtoh16(cfg.params.cur_bssid_bonus)); |
| return 0; |
| } |
| |
| /* process a SET */ |
| cfg.version = htod16(WL_PFN_SSID_CFG_VERSION); |
| cfg.flags = 0; |
| cfg.params.band_5g_bonus = 0; |
| cfg.params.secure_bonus = 0; |
| cfg.params.same_ssid_bonus = 0; |
| cfg.params.init_score_max = htod16(DEFAULT_INIT_SCORE_MAX); |
| cfg.params.cur_bssid_bonus = 0; |
| cfg.params.min2G_rssi = DEFAULT_MIN_2G_RSSI; |
| cfg.params.min5G_rssi = DEFAULT_MIN_5G_RSSI; |
| while (*++argv) { |
| if (!stricmp(*argv, "min5g_rssi")) { |
| if (*++argv) { |
| int rssi = strtol(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| if (rssi >= -128 && rssi <= 0) { |
| cfg.params.min5G_rssi = rssi; |
| } else { |
| fprintf(stderr, "Invalid rssi option %s\n", *argv); |
| err = BCME_BADARG; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for rssi\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| |
| } else if (!stricmp(*argv, "min2g_rssi")) { |
| if (*++argv) { |
| int rssi = strtol(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| if (rssi >= -128 && rssi <= 0) { |
| cfg.params.min2G_rssi = rssi; |
| } else { |
| fprintf(stderr, "Invalid rssi option %s\n", *argv); |
| err = BCME_BADARG; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for rssi\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "init_score_max")) { |
| if (*++argv) { |
| val = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| cfg.params.init_score_max = htod16(val); |
| } else { |
| fprintf(stderr, "Missing option for init_score_max\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "cur_bssid_bonus")) { |
| if (*++argv) { |
| val = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| cfg.params.cur_bssid_bonus = htod16(val); |
| } else { |
| fprintf(stderr, "Missing option for cur_bssid_bonus\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| |
| } else if (!stricmp(*argv, "same_ssid_bonus")) { |
| if (*++argv) { |
| val = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| cfg.params.same_ssid_bonus = htod16(val); |
| } else { |
| fprintf(stderr, "Missing option for same_ssid_bonus\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| |
| } else if (!stricmp(*argv, "secure_bonus")) { |
| if (*++argv) { |
| val = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| cfg.params.secure_bonus = htod16(val); |
| } else { |
| fprintf(stderr, "Missing option for secure_bonus\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "band_5g_bonus")) { |
| if (*++argv) { |
| val = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') |
| return BCME_USAGE_ERROR; |
| cfg.params.band_5g_bonus = htod16(val); |
| } else { |
| fprintf(stderr, "Missing option for band_5g_bonus\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| |
| } else if (!stricmp(*argv, "clear")) { |
| cfg.flags = WL_PFN_SSID_CFG_CLEAR; |
| if (i) { |
| fprintf(stderr, "Params will ONLY be cleared\n"); |
| } |
| } |
| i++; |
| } |
| if ((err = wlu_iovar_set(wl, "pfn_ssid_cfg", &cfg, |
| sizeof(wl_pfn_ssid_cfg_t)))) { |
| fprintf(stderr, "Failed to set pfn_ssid_cfg %d\n", err); |
| } |
| error: |
| return err; |
| } |
| |
| #define MAXNUM_BSSID_PER_ADD 180 |
| |
| static int |
| wl_pfn_add_bssid(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| uint8 *ptr; |
| int i, bssidlistlen, cnt; |
| wl_pfn_bssid_t *bssidlist; |
| wl_pfn_bssid_t *pbssid = NULL; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (!*(argv + 1)) { |
| fprintf(stderr, "Invalid command\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| bssidlistlen = MAXNUM_BSSID_PER_ADD * sizeof(wl_pfn_bssid_t); |
| bssidlist = (wl_pfn_bssid_t *)malloc(bssidlistlen); |
| if (bssidlist == NULL) { |
| fprintf(stderr, "Failed to allocate buffer for %d bytes\n", bssidlistlen); |
| return BCME_NOMEM; |
| } |
| memset(bssidlist, '\0', bssidlistlen); |
| |
| cnt = 0; |
| while (*++argv) { |
| if (!stricmp(*argv, "bssid")) { |
| if (*++argv) { |
| if (cnt >= MAXNUM_BSSID_PER_ADD) { |
| fprintf(stderr, "exceed max %d BSSID per pfn_add_bssid\n", |
| MAXNUM_BSSID_PER_ADD); |
| err = BCME_BADARG; |
| goto error; |
| } |
| if (!cnt) |
| pbssid = bssidlist; |
| else { |
| pbssid->flags = htod16(pbssid->flags); |
| pbssid++; |
| } |
| |
| ptr = (uint8 *)*argv; |
| for (i = 0; i < ETHER_ADDR_LEN; i++) |
| { |
| if (!validate_hex(*ptr) || !validate_hex(*(ptr + 1))) |
| { |
| fprintf(stderr, "non-hex in BSSID\n"); |
| err = BCME_BADARG; |
| goto error; |
| } |
| pbssid->macaddr.octet[i] = |
| char2hex(*ptr) << 4 | char2hex(*(ptr+1)); |
| ptr += 3; |
| } |
| cnt++; |
| } else { |
| fprintf(stderr, "Missing option for bssid\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "suppress")) { |
| if (!pbssid || ETHER_ISNULLADDR(pbssid->macaddr.octet)) { |
| fprintf(stderr, "Wrong! Start with BSSID\n"); |
| err = BCME_BADARG; |
| goto error; |
| } |
| if (*++argv) { |
| if (!stricmp(*argv, "found")) { |
| pbssid->flags |= WL_PFN_SUPPRESSFOUND_MASK; |
| } else if (!stricmp(*argv, "lost")) { |
| pbssid->flags |= WL_PFN_SUPPRESSLOST_MASK; |
| } else if (!stricmp(*argv, "neither")) { |
| pbssid->flags &= |
| ~(WL_PFN_SUPPRESSFOUND_MASK | WL_PFN_SUPPRESSLOST_MASK); |
| } else { |
| fprintf(stderr, "Invalid suppress option %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for suppress\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else if (!stricmp(*argv, "rssi")) { |
| if (*++argv) { |
| int rssi = atoi(*argv); |
| if (!pbssid || ETHER_ISNULLADDR(pbssid->macaddr.octet)) { |
| fprintf(stderr, "Wrong! Start with BSSID\n"); |
| err = BCME_BADARG; |
| goto error; |
| } |
| if (rssi >= -128 && rssi <= 0) { |
| pbssid->flags |= (rssi << WL_PFN_RSSI_SHIFT) |
| & WL_PFN_RSSI_MASK; |
| } else { |
| fprintf(stderr, "Invalid rssi option %s\n", *argv); |
| err = BCME_BADARG; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Missing option for rssi\n"); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| err = BCME_USAGE_ERROR; |
| goto error; |
| } |
| } |
| pbssid->flags = htod16(pbssid->flags); |
| |
| bssidlistlen = cnt * sizeof(wl_pfn_bssid_t); |
| if ((err = wlu_iovar_setbuf(wl, "pfn_add_bssid", bssidlist, |
| bssidlistlen, buf, ETHER_MAX_LEN))) { |
| fprintf(stderr, "pfn_add_bssid fail\n"); |
| goto error; |
| } |
| free(bssidlist); |
| return 0; |
| |
| error: |
| free(bssidlist); |
| return err; |
| } |
| |
| static int |
| wl_pfn_cfg(void *wl, cmd_t *cmd, char **argv) |
| { |
| wl_pfn_cfg_t pfncfg_param; |
| int nchan = 0; |
| int err; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| memset(&pfncfg_param, '\0', sizeof(wl_pfn_cfg_t)); |
| |
| /* Setup default values */ |
| pfncfg_param.reporttype = WL_PFN_REPORT_ALLNET; |
| pfncfg_param.channel_num = 0; |
| |
| while (*++argv) { |
| if (!stricmp(*argv, "report")) { |
| if (*++argv) { |
| if (!stricmp(*argv, "all")) { |
| pfncfg_param.reporttype = WL_PFN_REPORT_ALLNET; |
| } else if (!stricmp(*argv, "ssidonly")) { |
| pfncfg_param.reporttype = WL_PFN_REPORT_SSIDNET; |
| } else if (!stricmp(*argv, "bssidonly")) { |
| pfncfg_param.reporttype = WL_PFN_REPORT_BSSIDNET; |
| } else { |
| fprintf(stderr, "Invalid report option %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "no value for report\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "channel")) { |
| if (*++argv) { |
| nchan = wl_parse_channel_list(*argv, pfncfg_param.channel_list, |
| WL_NUMCHANNELS); |
| if (nchan < 0) { |
| fprintf(stderr, "error parsing channel\n"); |
| return BCME_BADARG; |
| } |
| } else { |
| fprintf(stderr, "Missing option for channel\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "prohibited")) { |
| if (*++argv) { |
| pfncfg_param.flags &= ~WL_PFN_CFG_FLAGS_PROHIBITED; |
| if (atoi(*argv)) |
| pfncfg_param.flags |= WL_PFN_CFG_FLAGS_PROHIBITED; |
| } else { |
| fprintf(stderr, "Missing prohibited option value\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else if (!stricmp(*argv, "history_off")) { |
| if (*++argv) { |
| pfncfg_param.flags &= ~WL_PFN_CFG_FLAGS_HISTORY_OFF; |
| if (atoi(*argv)) |
| pfncfg_param.flags |= WL_PFN_CFG_FLAGS_HISTORY_OFF; |
| } else { |
| fprintf(stderr, "Missing history_off option value\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } else { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| } |
| |
| pfncfg_param.reporttype = htod32(pfncfg_param.reporttype); |
| pfncfg_param.channel_num = htod32(nchan); |
| pfncfg_param.flags = htod32(pfncfg_param.flags); |
| |
| if ((err = wlu_iovar_set(wl, "pfn_cfg", &pfncfg_param, |
| sizeof(wl_pfn_cfg_t)))) { |
| fprintf(stderr, "pfn_cfg fail\n"); |
| return err; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| wl_pfn(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| val = atoi(*argv); |
| err = wlu_iovar_setint(wl, "pfn", (val ? 1 : 0)); |
| } else { |
| err = wlu_iovar_getint(wl, "pfn", &val); |
| if (!err) |
| wl_printint(val); |
| } |
| |
| return err; |
| } |
| |
| /* Use MEDLEN for efficient read, pre-calculate maximum possible nets for later checks */ |
| #define WL_MAX_BESTNET_V1 ((WLC_IOCTL_MEDLEN - OFFSETOF(wl_pfn_scanresults_v1_t, netinfo)) \ |
| / sizeof(wl_pfn_net_info_v1_t)) |
| #define WL_MAX_BESTNET_V2 ((WLC_IOCTL_MEDLEN - OFFSETOF(wl_pfn_scanresults_v2_t, netinfo)) \ |
| / sizeof(wl_pfn_net_info_v2_t)) |
| |
| static int |
| wl_pfnbest(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_scanresults_v1_t *bestnet_v1; |
| wl_pfn_net_info_v1_t *netinfo_v1; |
| wl_pfn_scanresults_v2_t *bestnet_v2; |
| wl_pfn_net_info_v2_t *netinfo_v2; |
| uint status; |
| uint32 i, j; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| |
| /* Use generic buffer to read and parse results */ |
| bestnet_v1 = (wl_pfn_scanresults_v1_t *)buf; |
| bestnet_v2 = (wl_pfn_scanresults_v2_t *)buf; |
| |
| /* Read results until completion indicated */ |
| do { |
| bzero(buf, WLC_IOCTL_MEDLEN); |
| |
| if ((err = wlu_iovar_get(wl, "pfnbest", (void*)buf, WLC_IOCTL_MEDLEN))) { |
| fprintf(stderr, "pfnbest fail\n"); |
| return err; |
| } |
| |
| if (bestnet_v2->version == PFN_SCANRESULTS_VERSION_V2) { |
| /* Use version 2 variables for processing */ |
| status = bestnet_v2->status; |
| if (bestnet_v2->count > WL_MAX_BESTNET_V2) { |
| fprintf(stderr, "Invalid data, count %d exceeds max buflen\n", |
| bestnet_v2->count); |
| return BCME_ERROR; |
| } |
| |
| printf("ver %d, status %d, count %d\n", |
| bestnet_v2->version, bestnet_v2->status, bestnet_v2->count); |
| netinfo_v2 = bestnet_v2->netinfo; |
| for (i = 0; i < bestnet_v2->count; i++) { |
| for (j = 0; j < netinfo_v2->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v2->pfnsubnet.u.SSID[j]); |
| printf("\n"); |
| printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v2->pfnsubnet.BSSID.octet[0], |
| netinfo_v2->pfnsubnet.BSSID.octet[1], |
| netinfo_v2->pfnsubnet.BSSID.octet[2], |
| netinfo_v2->pfnsubnet.BSSID.octet[3], |
| netinfo_v2->pfnsubnet.BSSID.octet[4], |
| netinfo_v2->pfnsubnet.BSSID.octet[5]); |
| printf("channel: %d, RSSI: %d, timestamp: %d\n", |
| netinfo_v2->pfnsubnet.channel, netinfo_v2->RSSI, |
| netinfo_v2->timestamp); |
| netinfo_v2++; |
| } |
| } else if (bestnet_v1->version == PFN_SCANRESULTS_VERSION_V1) { |
| /* Use version 1 variables for processing */ |
| status = bestnet_v1->status; |
| if (bestnet_v1->count > WL_MAX_BESTNET_V1) { |
| fprintf(stderr, "Invalid data, count %d exceeds max buflen\n", |
| bestnet_v1->count); |
| return BCME_ERROR; |
| } |
| |
| printf("ver %d, status %d, count %d\n", |
| bestnet_v1->version, bestnet_v1->status, bestnet_v1->count); |
| netinfo_v1 = bestnet_v1->netinfo; |
| for (i = 0; i < bestnet_v1->count; i++) { |
| for (j = 0; j < netinfo_v1->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v1->pfnsubnet.SSID[j]); |
| printf("\n"); |
| printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v1->pfnsubnet.BSSID.octet[0], |
| netinfo_v1->pfnsubnet.BSSID.octet[1], |
| netinfo_v1->pfnsubnet.BSSID.octet[2], |
| netinfo_v1->pfnsubnet.BSSID.octet[3], |
| netinfo_v1->pfnsubnet.BSSID.octet[4], |
| netinfo_v1->pfnsubnet.BSSID.octet[5]); |
| printf("channel: %d, RSSI: %d, timestamp: %d\n", |
| netinfo_v1->pfnsubnet.channel, netinfo_v1->RSSI, |
| netinfo_v1->timestamp); |
| netinfo_v1++; |
| } |
| } else { |
| fprintf(stderr, "Unrecognized version %d\n", bestnet_v1->version); |
| return BCME_ERROR; |
| } |
| } while (status != PFN_COMPLETE); |
| |
| return 0; |
| } |
| |
| /* Use MEDLEN for efficient read, pre-calculate maximum possible nets for later checks */ |
| #define WL_MAX_LBESTNET_V1 ((WLC_IOCTL_MEDLEN - OFFSETOF(wl_pfn_lscanresults_v1_t, netinfo)) \ |
| / sizeof(wl_pfn_net_info_v1_t)) |
| #define WL_MAX_LBESTNET_V2 ((WLC_IOCTL_MEDLEN - OFFSETOF(wl_pfn_lscanresults_v2_t, netinfo)) \ |
| / sizeof(wl_pfn_lnet_info_v2_t)) |
| |
| static int |
| wl_pfnlbest(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_lscanresults_v1_t *bestnet_v1; |
| wl_pfn_lnet_info_v1_t *netinfo_v1; |
| wl_pfn_lscanresults_v2_t *bestnet_v2; |
| wl_pfn_lnet_info_v2_t *netinfo_v2; |
| uint status; |
| uint32 i, j; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| |
| /* Use generic buffer to read and parse results */ |
| bestnet_v1 = (wl_pfn_lscanresults_v1_t *)buf; |
| bestnet_v2 = (wl_pfn_lscanresults_v2_t *)buf; |
| |
| /* Read results until completion indicated */ |
| do { |
| bzero(buf, WLC_IOCTL_MEDLEN); |
| |
| if ((err = wlu_iovar_get(wl, "pfnlbest", (void *)buf, WLC_IOCTL_MEDLEN))) { |
| fprintf(stderr, "pfnlbest fail\n"); |
| return err; |
| } |
| |
| if (bestnet_v2->version == PFN_LBEST_SCAN_RESULT_VERSION_V2) { |
| /* Use version 2 variables for processing */ |
| status = bestnet_v2->status; |
| if (bestnet_v2->count > WL_MAX_LBESTNET_V2) { |
| fprintf(stderr, "Invalid data, count %d exceeds max buflen\n", |
| bestnet_v2->count); |
| return BCME_ERROR; |
| } |
| |
| printf("ver %d, status %d, count %d\n", |
| bestnet_v2->version, bestnet_v2->status, bestnet_v2->count); |
| netinfo_v2 = bestnet_v2->netinfo; |
| for (i = 0; i < bestnet_v2->count; i++) { |
| for (j = 0; j < netinfo_v2->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v2->pfnsubnet.u.SSID[j]); |
| printf("\n"); |
| printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v2->pfnsubnet.BSSID.octet[0], |
| netinfo_v2->pfnsubnet.BSSID.octet[1], |
| netinfo_v2->pfnsubnet.BSSID.octet[2], |
| netinfo_v2->pfnsubnet.BSSID.octet[3], |
| netinfo_v2->pfnsubnet.BSSID.octet[4], |
| netinfo_v2->pfnsubnet.BSSID.octet[5]); |
| printf("channel: %d, flags: %d, RSSI: %d, timestamp: %d\n", |
| netinfo_v2->pfnsubnet.channel, netinfo_v2->flags, |
| netinfo_v2->RSSI, netinfo_v2->timestamp); |
| printf("RTT0: %d, RTT1: %d\n", netinfo_v2->rtt0, netinfo_v2->rtt1); |
| netinfo_v2++; |
| } |
| } else if (bestnet_v1->version == PFN_LBEST_SCAN_RESULT_VERSION_V1) { |
| /* Use version 1 variables for processing */ |
| status = bestnet_v1->status; |
| if (bestnet_v1->count > WL_MAX_LBESTNET_V1) { |
| fprintf(stderr, "Invalid data, count %d exceeds max buflen\n", |
| bestnet_v1->count); |
| return BCME_ERROR; |
| } |
| |
| printf("ver %d, status %d, count %d\n", |
| bestnet_v1->version, bestnet_v1->status, bestnet_v1->count); |
| netinfo_v1 = bestnet_v1->netinfo; |
| for (i = 0; i < bestnet_v1->count; i++) { |
| for (j = 0; j < netinfo_v1->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v1->pfnsubnet.SSID[j]); |
| printf("\n"); |
| printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v1->pfnsubnet.BSSID.octet[0], |
| netinfo_v1->pfnsubnet.BSSID.octet[1], |
| netinfo_v1->pfnsubnet.BSSID.octet[2], |
| netinfo_v1->pfnsubnet.BSSID.octet[3], |
| netinfo_v1->pfnsubnet.BSSID.octet[4], |
| netinfo_v1->pfnsubnet.BSSID.octet[5]); |
| printf("channel: %d, flags: %d, RSSI: %d, timestamp: %d\n", |
| netinfo_v1->pfnsubnet.channel, netinfo_v1->flags, |
| netinfo_v1->RSSI, netinfo_v1->timestamp); |
| printf("RTT0: %d, RTT1: %d\n", netinfo_v1->rtt0, netinfo_v1->rtt1); |
| netinfo_v1++; |
| } |
| } else { |
| fprintf(stderr, "Unrecognized version %d\n", bestnet_v1->version); |
| return BCME_ERROR; |
| } |
| } while (status == PFN_INCOMPLETE); |
| |
| return 0; |
| } |
| |
| static int |
| wl_pfnbest_bssid(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_scanhist_bssid_t *bestnet; |
| wl_pfn_net_info_bssid_t *netinfo; |
| uint32 i; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| fprintf(stderr, "Invalid parameter %s\n", *argv); |
| return BCME_ERROR; |
| } |
| |
| /* Use generic buffer to read and parse results */ |
| bestnet = (wl_pfn_scanhist_bssid_t *)buf; |
| |
| do { |
| memset(bestnet, 0, WLC_IOCTL_MEDLEN); |
| |
| if ((err = wlu_iovar_get(wl, "pfnbest_bssid", |
| (void *)bestnet, WLC_IOCTL_MEDLEN))) { |
| fprintf(stderr, "pfnbest_bssid fail\n"); |
| return err; |
| } |
| |
| printf("ver %d, status %d, count %d\n", |
| bestnet->version, bestnet->status, bestnet->count); |
| netinfo = bestnet->netinfo; |
| for (i = 0; i < bestnet->count; i++) { |
| printf("%02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo->BSSID.octet[0], |
| netinfo->BSSID.octet[1], |
| netinfo->BSSID.octet[2], |
| netinfo->BSSID.octet[3], |
| netinfo->BSSID.octet[4], |
| netinfo->BSSID.octet[5]); |
| printf("channel: %d, RSSI: %d, timestamp: %d\n", |
| netinfo->channel, netinfo->RSSI, netinfo->timestamp); |
| netinfo++; |
| } |
| } while (bestnet->status != PFN_COMPLETE); |
| |
| return 0; |
| } |
| |
| static int |
| wl_pfn_suspend(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| val = atoi(*argv); |
| err = wlu_iovar_setint(wl, "pfn_suspend", (val ? 1 : 0)); |
| } else { |
| err = wlu_iovar_getint(wl, "pfn_suspend", &val); |
| if (!err) |
| wl_printint(val); |
| } |
| |
| return err; |
| } |
| |
| static int |
| wl_pfn_mem(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv && !stricmp(*argv, "bestn")) { |
| if (*++argv) |
| val = atoi(*argv); |
| else { |
| fprintf(stderr, "Missing bestn value\n"); |
| return -1; |
| } |
| } else { |
| fprintf(stderr, "Missing bestn option\n"); |
| return -1; |
| } |
| |
| err = wlu_iovar_setint(wl, "pfnmem", val); |
| if (err) { |
| fprintf(stderr, "pfnmem set wrong!\n"); |
| return err; |
| } |
| |
| err = wlu_iovar_getint(wl, "pfnmem", &val); |
| if (!err) |
| wl_printint(val); |
| else |
| fprintf(stderr, "pfnmem get wrong!\n"); |
| return err; |
| } |
| |
| #if defined(linux) |
| static void |
| wl_pfn_printnet(void *ptr, int event_type) |
| { |
| wl_pfn_net_info_v1_t *netinfo_v1; |
| wl_pfn_net_info_v2_t *netinfo_v2; |
| uint32 i, j; |
| |
| if (WLC_E_PFN_NET_FOUND == event_type) { |
| printf("WLC_E_PFN_NET_FOUND:\n"); |
| } else if (WLC_E_PFN_NET_LOST == event_type) { |
| printf("WLC_E_PFN_NET_LOST:\n"); |
| } else if (WLC_E_PFN_BSSID_NET_FOUND == event_type) { |
| printf("WLC_E_PFN_BSSID_NET_FOUND:\n"); |
| } else if (WLC_E_PFN_BSSID_NET_LOST == event_type) { |
| printf("WLC_E_PFN_BSSID_NET_LOST:\n"); |
| } else { |
| return; |
| } |
| |
| if (((wl_pfn_scanresults_v2_t *)ptr)->version == PFN_SCANRESULTS_VERSION_V2) { |
| netinfo_v2 = ((wl_pfn_scanresults_v2_t *)ptr)->netinfo; |
| printf("ver %d, status %d, count %d\n", |
| ((wl_pfn_scanresults_v2_t *)ptr)->version, |
| ((wl_pfn_scanresults_v2_t *)ptr)->status, |
| ((wl_pfn_scanresults_v2_t *)ptr)->count); |
| for (i = 0; i < ((wl_pfn_scanresults_v2_t *)ptr)->count; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < netinfo_v2->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v2->pfnsubnet.u.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v2->pfnsubnet.BSSID.octet[0], |
| netinfo_v2->pfnsubnet.BSSID.octet[1], |
| netinfo_v2->pfnsubnet.BSSID.octet[2], |
| netinfo_v2->pfnsubnet.BSSID.octet[3], |
| netinfo_v2->pfnsubnet.BSSID.octet[4], |
| netinfo_v2->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| netinfo_v2->pfnsubnet.channel, netinfo_v2->RSSI, |
| netinfo_v2->timestamp); |
| netinfo_v2++; |
| } |
| } else if (((wl_pfn_scanresults_v1_t *)ptr)->version == PFN_SCANRESULTS_VERSION_V1) { |
| netinfo_v1 = ((wl_pfn_scanresults_v1_t *)ptr)->netinfo; |
| printf("ver %d, status %d, count %d\n", |
| ((wl_pfn_scanresults_v1_t *)ptr)->version, |
| ((wl_pfn_scanresults_v1_t *)ptr)->status, |
| ((wl_pfn_scanresults_v1_t *)ptr)->count); |
| for (i = 0; i < ((wl_pfn_scanresults_v1_t *)ptr)->count; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < netinfo_v1->pfnsubnet.SSID_len; j++) |
| printf("%c", netinfo_v1->pfnsubnet.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| netinfo_v1->pfnsubnet.BSSID.octet[0], |
| netinfo_v1->pfnsubnet.BSSID.octet[1], |
| netinfo_v1->pfnsubnet.BSSID.octet[2], |
| netinfo_v1->pfnsubnet.BSSID.octet[3], |
| netinfo_v1->pfnsubnet.BSSID.octet[4], |
| netinfo_v1->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| netinfo_v1->pfnsubnet.channel, netinfo_v1->RSSI, |
| netinfo_v1->timestamp); |
| |
| netinfo_v1++; |
| } |
| } |
| } |
| |
| static int |
| wl_pfn_event_check(void *wl, cmd_t *cmd, char **argv) |
| { |
| int fd, err; |
| struct sockaddr_ll sll; |
| struct ifreq ifr; |
| char ifnames[IFNAMSIZ] = {"eth1"}; |
| bcm_event_t * event; |
| char data[512]; |
| int event_type; |
| struct ether_addr *addr; |
| char eabuf[ETHER_ADDR_STR_LEN]; |
| wl_pfn_scanresults_v1_t *ptr_v1; |
| wl_pfn_net_info_v1_t *info_v1; |
| wl_pfn_scanresults_v2_t *ptr_v2; |
| wl_pfn_net_info_v2_t *info_v2; |
| uint32 i, j; |
| uint32 foundcnt, lostcnt; |
| |
| UNUSED_PARAMETER(wl); |
| UNUSED_PARAMETER(cmd); |
| |
| /* Override default ifname explicitly or implicitly */ |
| if (*++argv) { |
| if (strlen(*argv) >= IFNAMSIZ) { |
| printf("Interface name %s too long\n", *argv); |
| return BCME_USAGE_ERROR; |
| } |
| strncpy(ifnames, *argv, IFNAMSIZ); |
| } else { |
| strncpy(ifnames, ((struct ifreq *)wl)->ifr_name, (IFNAMSIZ - 1)); |
| } |
| |
| ifnames[IFNAMSIZ - 1] = '\0'; |
| |
| memset(&ifr, 0, sizeof(ifr)); |
| strncpy(ifr.ifr_name, ifnames, IFNAMSIZ); |
| |
| fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM)); |
| if (fd < 0) { |
| printf("Cannot create socket %d\n", fd); |
| return BCME_ERROR; |
| } |
| |
| err = ioctl(fd, SIOCGIFINDEX, &ifr); |
| if (err < 0) { |
| printf("Cannot get index %d\n", err); |
| close(fd); |
| return BCME_ERROR; |
| } |
| |
| memset(&sll, 0, sizeof(sll)); |
| sll.sll_family = AF_PACKET; |
| sll.sll_protocol = hton16(ETHER_TYPE_BRCM); |
| sll.sll_ifindex = ifr.ifr_ifindex; |
| err = bind(fd, (struct sockaddr *)&sll, sizeof(sll)); |
| if (err < 0) { |
| printf("Cannot get index %d\n", err); |
| close(fd); |
| return BCME_ERROR; |
| } |
| |
| /* Pre-set the results pointers for any data we might receive */ |
| ptr_v1 = (wl_pfn_scanresults_v1_t *)(data + sizeof(bcm_event_t)); |
| ptr_v2 = (wl_pfn_scanresults_v2_t *)(data + sizeof(bcm_event_t)); |
| |
| while (1) { |
| recv(fd, data, sizeof(data), 0); |
| event = (bcm_event_t *)data; |
| addr = (struct ether_addr *)&(event->event.addr); |
| |
| event_type = ntoh32(event->event.event_type); |
| |
| if (addr != NULL) { |
| sprintf(eabuf, "%02x:%02x:%02x:%02x:%02x:%02x", |
| (uchar)addr->octet[0]&0xff, |
| (uchar)addr->octet[1]&0xff, |
| (uchar)addr->octet[2]&0xff, |
| (uchar)addr->octet[3]&0xff, |
| (uchar)addr->octet[4]&0xff, |
| (uchar)addr->octet[5]&0xff); |
| } |
| |
| if (ntoh32(event->event.datalen)) { |
| if (WLC_E_PFN_SCAN_COMPLETE == event_type) { |
| /* Version check for PFN */ |
| if (ptr_v2->version == PFN_SCANRESULTS_VERSION_V2) { |
| info_v2 = ptr_v2->netinfo; |
| foundcnt = ptr_v2->count & 0xffff; |
| lostcnt = ptr_v2->count >> 16; |
| printf("ver %d, status %d, found %d, lost %d\n", |
| ptr_v2->version, ptr_v2->status, |
| foundcnt, lostcnt); |
| if (foundcnt) |
| printf("Network found:\n"); |
| for (i = 0; i < foundcnt; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < info_v2->pfnsubnet.SSID_len; j++) |
| printf("%c", info_v2->pfnsubnet.u.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| info_v2->pfnsubnet.BSSID.octet[0], |
| info_v2->pfnsubnet.BSSID.octet[1], |
| info_v2->pfnsubnet.BSSID.octet[2], |
| info_v2->pfnsubnet.BSSID.octet[3], |
| info_v2->pfnsubnet.BSSID.octet[4], |
| info_v2->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| info_v2->pfnsubnet.channel, info_v2->RSSI, |
| info_v2->timestamp); |
| info_v2++; |
| } |
| if (lostcnt) |
| printf("Network lost:\n"); |
| for (i = 0; i < lostcnt; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < info_v2->pfnsubnet.SSID_len; j++) |
| printf("%c", info_v2->pfnsubnet.u.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| info_v2->pfnsubnet.BSSID.octet[0], |
| info_v2->pfnsubnet.BSSID.octet[1], |
| info_v2->pfnsubnet.BSSID.octet[2], |
| info_v2->pfnsubnet.BSSID.octet[3], |
| info_v2->pfnsubnet.BSSID.octet[4], |
| info_v2->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| info_v2->pfnsubnet.channel, info_v2->RSSI, |
| info_v2->timestamp); |
| info_v2++; |
| } |
| } else if (ptr_v1->version == PFN_SCANRESULTS_VERSION_V1) { |
| info_v1 = ptr_v1->netinfo; |
| foundcnt = ptr_v1->count & 0xffff; |
| lostcnt = ptr_v1->count >> 16; |
| printf("ver %d, status %d, found %d, lost %d\n", |
| ptr_v1->version, ptr_v1->status, |
| foundcnt, lostcnt); |
| if (foundcnt) |
| printf("Network found:\n"); |
| for (i = 0; i < foundcnt; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < info_v1->pfnsubnet.SSID_len; j++) |
| printf("%c", info_v1->pfnsubnet.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| info_v1->pfnsubnet.BSSID.octet[0], |
| info_v1->pfnsubnet.BSSID.octet[1], |
| info_v1->pfnsubnet.BSSID.octet[2], |
| info_v1->pfnsubnet.BSSID.octet[3], |
| info_v1->pfnsubnet.BSSID.octet[4], |
| info_v1->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| info_v1->pfnsubnet.channel, info_v1->RSSI, |
| info_v1->timestamp); |
| info_v1++; |
| } |
| if (lostcnt) |
| printf("Network lost:\n"); |
| for (i = 0; i < lostcnt; i++) { |
| printf("%d. ", i + 1); |
| for (j = 0; j < info_v1->pfnsubnet.SSID_len; j++) |
| printf("%c", info_v1->pfnsubnet.SSID[j]); |
| printf("\n"); |
| printf("BSSID %02x:%02x:%02x:%02x:%02x:%02x\n", |
| info_v1->pfnsubnet.BSSID.octet[0], |
| info_v1->pfnsubnet.BSSID.octet[1], |
| info_v1->pfnsubnet.BSSID.octet[2], |
| info_v1->pfnsubnet.BSSID.octet[3], |
| info_v1->pfnsubnet.BSSID.octet[4], |
| info_v1->pfnsubnet.BSSID.octet[5]); |
| printf("channel %d, RSSI %d, timestamp %d\n", |
| info_v1->pfnsubnet.channel, info_v1->RSSI, |
| info_v1->timestamp); |
| info_v1++; |
| } |
| } else { |
| fprintf(stderr, "Unrecognized version %d\n", |
| ptr_v1->version); |
| return BCME_ERROR; |
| } |
| } else if ((WLC_E_PFN_NET_FOUND == event_type) || |
| (WLC_E_PFN_NET_LOST == event_type) || |
| (WLC_E_PFN_BSSID_NET_FOUND == event_type) || |
| (WLC_E_PFN_BSSID_NET_LOST == event_type)) { |
| wl_pfn_printnet((void *)(data + sizeof(bcm_event_t)), event_type); |
| } |
| |
| if (WLC_E_LINK == event_type || WLC_E_NDIS_LINK == event_type) { |
| if (ntoh16(event->event.flags) & WLC_EVENT_MSG_LINK) |
| printf("MACEVENT Link up :%s\n", eabuf); |
| else |
| printf("MACEVENT Link down :%s\n", eabuf); |
| } |
| } else { |
| if (WLC_E_PFN_SCAN_NONE == event_type) { |
| printf("Got WLC_E_PFN_SCAN_NONE\n"); |
| } |
| if (WLC_E_PFN_SCAN_ALLGONE == event_type) { |
| printf("Got WLC_E_PFN_SCAN_ALLGONE\n"); |
| } |
| if (WLC_E_PFN_BEST_BATCHING == event_type) { |
| printf("Got WLC_E_PFN_BEST_BATCHING\n"); |
| } |
| } |
| } |
| |
| close(fd); |
| return (0); |
| } |
| #endif /* linux */ |
| |
| static int |
| wl_event_filter(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */ |
| |
| UNUSED_PARAMETER(cmd); |
| UNUSED_PARAMETER(argv); |
| |
| memset(event_inds_mask, '\0', WL_EVENTING_MASK_LEN); |
| |
| /* Register for following event for pfn */ |
| event_inds_mask[WLC_E_LINK / 8] |= 1 << (WLC_E_LINK % 8); |
| event_inds_mask[WLC_E_PFN_NET_FOUND / 8] |= 1 << (WLC_E_PFN_NET_FOUND % 8); |
| event_inds_mask[WLC_E_PFN_NET_LOST / 8] |= 1 << (WLC_E_PFN_NET_LOST % 8); |
| event_inds_mask[WLC_E_PFN_SCAN_NONE/ 8] |= 1 << (WLC_E_PFN_SCAN_NONE % 8); |
| event_inds_mask[WLC_E_PFN_SCAN_ALLGONE/ 8] |= 1 << (WLC_E_PFN_SCAN_ALLGONE % 8); |
| |
| if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) |
| return (err); |
| |
| return (0); |
| } |
| |
| static int |
| wl_pfn_roam_alert_thresh(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, buflen; |
| wl_pfn_roam_thresh_t *pfn_roam_alert; |
| |
| buflen = sprintf(buf, "%s", *argv) + 1; |
| |
| if (*++(argv) == NULL) { |
| buf[buflen] = '\0'; |
| err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN); |
| if (err < 0) |
| return err; |
| |
| pfn_roam_alert = (wl_pfn_roam_thresh_t *)buf; |
| printf("pfn_alert_thresh %u\n", pfn_roam_alert->pfn_alert_thresh); |
| printf("roam_alert_thresh %u\n", pfn_roam_alert->roam_alert_thresh); |
| return 0; |
| |
| } else { |
| pfn_roam_alert = (wl_pfn_roam_thresh_t *) (buf + buflen); |
| buflen += sizeof(wl_pfn_roam_thresh_t); |
| |
| pfn_roam_alert->pfn_alert_thresh = (uint32) strtoul(*argv, NULL, 0); |
| |
| if (*++(argv) == NULL) { |
| printf("Incorrect number of arguments\n"); |
| return BCME_ERROR; |
| } |
| pfn_roam_alert->roam_alert_thresh = (uint32) strtoul(*argv, NULL, 0); |
| |
| if (*++(argv)) { |
| printf("extra arguments\n"); |
| return BCME_ERROR; |
| } |
| err = wlu_set(wl, WLC_SET_VAR, buf, buflen); |
| |
| return err; |
| } |
| return 0; |
| } |
| |
| static char *adaptname[] = { "off", "smart", "strict", "slow" }; |
| |
| static int |
| wl_pfn_override(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_override_param_t pfnov_params; |
| wl_pfn_override_param_t *pfnov_paramp; |
| wl_pfn_mpf_state_params_t *statep; |
| char *endptr; |
| |
| uint16 start, duration; |
| |
| /* Initialize the structure (both set and get) */ |
| memset(&pfnov_params, 0, sizeof(pfnov_params)); |
| pfnov_params.version = dtoh16(WL_PFN_OVERRIDE_VERSION); |
| |
| if (*++argv == NULL) { |
| /* No arguments: do a GET and display results */ |
| err = wlu_iovar_getbuf(wl, cmd->name, |
| &pfnov_params, sizeof(pfnov_params), |
| buf, WLC_IOCTL_MAXLEN); |
| if (err >= 0) { |
| pfnov_paramp = (wl_pfn_override_param_t *)buf; |
| if (dtoh16(pfnov_paramp->version) != WL_PFN_OVERRIDE_VERSION) { |
| fprintf(stderr, "Incorrect version (%d != %d).\n", |
| dtoh16(pfnov_paramp->version), WL_PFN_OVERRIDE_VERSION); |
| return -1; |
| } |
| start = dtoh16(pfnov_paramp->start_offset); |
| duration = dtoh16(pfnov_paramp->duration); |
| |
| if (duration) { |
| if (start == 0) |
| printf("Active, remaining duration %d\n", duration); |
| else |
| printf("Scheduled in %d, duration %d\n", start, duration); |
| |
| /* Disaply the actual parameters */ |
| statep = &pfnov_paramp->override; |
| statep->scan_freq = dtoh32(statep->scan_freq); |
| statep->lost_network_timeout = |
| dtoh32(statep->lost_network_timeout); |
| statep->flags = dtoh16(statep->flags); |
| statep->slow_freq = dtoh32(statep->slow_freq); |
| |
| printf("Scan frequency: %d\n", statep->scan_freq); |
| if (statep->lost_network_timeout) |
| printf("Net timeout: %d\n", |
| dtoh32(statep->lost_network_timeout)); |
| if (statep->flags & WL_PFN_MPF_ADAPT_ON_MASK) { |
| uint atype; |
| atype = statep->flags & WL_PFN_MPF_ADAPTSCAN_MASK; |
| atype = atype >> WL_PFN_MPF_ADAPTSCAN_BIT; |
| printf("Adapt: %s\n", adaptname[atype]); |
| } |
| if (statep->exp) |
| printf(" Exp: %d\n", statep->exp); |
| if (statep->repeat) |
| printf(" Repeat: %d\n", statep->repeat); |
| if (statep->slow_freq) |
| printf(" Slow: %d\n", statep->slow_freq); |
| } else { |
| printf("Override not configured\n"); |
| } |
| } |
| } else { |
| /* Additional arguments: parse and do a set */ |
| |
| /* Start field (required) */ |
| start = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric start value %s\n", *argv); |
| return -1; |
| } |
| |
| /* Duration field (required) */ |
| if (*++argv == NULL) { |
| fprintf(stderr, "Duration required\n"); |
| return -1; |
| } |
| duration = strtoul(*argv, &endptr, 0); |
| argv++; |
| |
| /* Allow duration and start both 0 to mean cancel, otherwise |
| * zero duration is meaningless. A nonzero duration requires |
| * a scan frequency at a minimum |
| */ |
| if (!duration) { |
| if (start) { |
| fprintf(stderr, "Start with no duration invalid\n"); |
| return -1; |
| } |
| } else { |
| /* Read the actual timing parameters */ |
| statep = &pfnov_params.override; |
| if ((*argv == NULL) || strcmp(*argv, "scanfrq")) { |
| fprintf(stderr, "scanfrq required as first parameter\n"); |
| return -1; |
| } |
| |
| if (argv[1] == NULL) { |
| fprintf(stderr, "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->scan_freq = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric scanfrq %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| |
| /* As long as there was a scan_freq, pick up other options */ |
| while (*argv && statep->scan_freq) { |
| if (*argv && !strcmp(*argv, "netimeout")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->lost_network_timeout = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric netimeout %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "adapt")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| if (!strcmp(*argv, "off")) { |
| statep->flags |= |
| (OFF_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "smart")) { |
| statep->flags |= |
| (SMART_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "strict")) { |
| statep->flags |= |
| (STRICT_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "slow")) { |
| statep->flags |= |
| (SLOW_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else { |
| fprintf(stderr, |
| "Invalid adaptive scan option %s\n", |
| *argv); |
| return -1; |
| } |
| statep->flags |= WL_PFN_MPF_ADAPT_ON_MASK; |
| argv++; |
| } else if (*argv && !strcmp(*argv, "repeat")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->repeat = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric repeat value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "exp")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->exp = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric exp value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "slowfrq")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->slow_freq = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric slowfrq value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else { |
| /* Not a recognized option, exit loop */ |
| break; |
| } |
| } |
| |
| statep->scan_freq = htod32(statep->scan_freq); |
| statep->lost_network_timeout = htod32(statep->lost_network_timeout); |
| statep->flags = htod16(statep->flags); |
| statep->slow_freq = htod32(statep->slow_freq); |
| } |
| |
| /* Should be no leftover args at this point */ |
| if (*argv) { |
| fprintf(stderr, "Input error at %s\n", *argv); |
| return -1; |
| } |
| |
| /* Fill in the start and duration fields */ |
| pfnov_params.start_offset = htod16(start); |
| pfnov_params.duration = htod16(duration); |
| |
| /* Now do the set */ |
| err = wlu_iovar_setbuf(wl, cmd->name, |
| &pfnov_params, sizeof(pfnov_params), buf, WLC_IOCTL_MAXLEN); |
| } |
| |
| return err; |
| } |
| |
| static int |
| wl_pfn_macaddr(void *wl, cmd_t *cmd, char **argv) |
| { |
| int ret = BCME_OK; |
| wl_pfn_macaddr_cfg_t pfn_mac; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| memset(&pfn_mac, 0, sizeof(pfn_mac)); |
| pfn_mac.version = WL_PFN_MACADDR_CFG_VER; |
| |
| if (argv[1] != NULL) { |
| if (!wl_ether_atoe(argv[1], &pfn_mac.macaddr)) { |
| fprintf(stderr, "Invalid MAC address %s\n", *argv); |
| return -1; |
| } |
| |
| return wlu_iovar_set(wl, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac)); |
| } else { |
| if ((ret = wlu_iovar_get(wl, "pfn_macaddr", &pfn_mac, sizeof(pfn_mac))) < 0) |
| return ret; |
| |
| printf("%s\n", wl_ether_etoa(&pfn_mac.macaddr)); |
| } |
| |
| return ret; |
| } |
| |
| #ifdef WL_MPF |
| static int |
| wl_pfn_mpfset(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_pfn_mpf_param_t mpf_params; |
| wl_pfn_mpf_param_t *mpf_paramp; |
| wl_pfn_mpf_state_params_t *statep; |
| char *endptr; |
| uint state; |
| |
| if (*++argv == NULL) { |
| fprintf(stderr, "Requires at least <groupid>\n"); |
| return -1; |
| } |
| |
| /* Initialize the structure (both set and get) */ |
| memset(&mpf_params, 0, sizeof(mpf_params)); |
| mpf_params.version = dtoh16(WL_PFN_MPF_VERSION); |
| mpf_params.groupid = dtoh16(strtoul(*argv, &endptr, 0)); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric groupid %s\n", *argv); |
| return -1; |
| } |
| |
| if (*++argv == NULL) { |
| /* No arguments beyond groupid: do a GET and display results */ |
| err = wlu_iovar_getbuf(wl, cmd->name, |
| &mpf_params, OFFSETOF(wl_pfn_mpf_param_t, state), |
| buf, WLC_IOCTL_MAXLEN); |
| if (err >= 0) { |
| mpf_paramp = (wl_pfn_mpf_param_t *)buf; |
| if (dtoh16(mpf_paramp->version) != WL_PFN_MPF_VERSION) { |
| fprintf(stderr, "Incorrect version (%d != %d).\n", |
| dtoh16(mpf_paramp->version), WL_PFN_MPF_VERSION); |
| return -1; |
| } |
| if (mpf_paramp->groupid != mpf_params.groupid) { |
| fprintf(stderr, "Groupid modified (%d -> %d)?\n", |
| dtoh16(mpf_params.groupid), dtoh16(mpf_paramp->groupid)); |
| return -1; |
| } |
| |
| for (state = 0, statep = mpf_paramp->state; |
| state < WL_PFN_MPF_STATES_MAX; state++, statep++) { |
| if (!statep->scan_freq) |
| continue; |
| |
| statep->scan_freq = dtoh32(statep->scan_freq); |
| statep->lost_network_timeout = |
| dtoh32(statep->lost_network_timeout); |
| statep->flags = dtoh16(statep->flags); |
| statep->slow_freq = dtoh32(statep->slow_freq); |
| |
| printf("State %d:\n" |
| " Scan frequency: %d\n", |
| state, statep->scan_freq); |
| if (statep->lost_network_timeout) |
| printf(" Net timeout: %d\n", |
| dtoh32(statep->lost_network_timeout)); |
| if (statep->flags & WL_PFN_MPF_ADAPT_ON_MASK) { |
| uint atype; |
| atype = statep->flags & WL_PFN_MPF_ADAPTSCAN_MASK; |
| atype = atype >> WL_PFN_MPF_ADAPTSCAN_BIT; |
| printf(" Adapt: %s\n", adaptname[atype]); |
| } |
| if (statep->exp) |
| printf(" Exp: %d\n", statep->exp); |
| if (statep->repeat) |
| printf(" Repeat: %d\n", statep->repeat); |
| if (statep->slow_freq) |
| printf(" Slow: %d\n", statep->slow_freq); |
| } |
| } |
| } else { |
| /* Additional arguments: parse and do a set */ |
| while (*argv && !strcmp(*argv, "state")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| state = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric state %s\n", *argv); |
| return -1; |
| } |
| if (state >= WL_PFN_MPF_STATES_MAX) { |
| fprintf(stderr, "Invalid state %d\n", state); |
| return -1; |
| } |
| argv++; |
| |
| /* Set up for this state and get the scan frequency */ |
| statep = &mpf_params.state[state]; |
| if (statep->scan_freq) { |
| fprintf(stderr, "Repeated state (%d)\n", state); |
| return -1; |
| } |
| |
| if (*argv && !strcmp(*argv, "scanfrq")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->scan_freq = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric scanfrq %s\n", *argv); |
| return -1; |
| } |
| } else { |
| fprintf(stderr, "State requires scanfrq\n"); |
| return -1; |
| } |
| argv++; |
| |
| /* If scan_freq is 0, don't allow other options */ |
| if (!statep->scan_freq) |
| continue; |
| |
| /* Pick up any other options */ |
| while (*argv) { |
| if (*argv && !strcmp(*argv, "netimeout")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->lost_network_timeout = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric netimeout %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "adapt")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| if (!strcmp(*argv, "off")) { |
| statep->flags |= |
| (OFF_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "smart")) { |
| statep->flags |= |
| (SMART_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "strict")) { |
| statep->flags |= |
| (STRICT_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else if (!strcmp(*argv, "slow")) { |
| statep->flags |= |
| (SLOW_ADAPT << WL_PFN_MPF_ADAPTSCAN_BIT); |
| } else { |
| fprintf(stderr, |
| "Invalid adaptive scan option %s\n", |
| *argv); |
| return -1; |
| } |
| statep->flags |= WL_PFN_MPF_ADAPT_ON_MASK; |
| argv++; |
| } else if (*argv && !strcmp(*argv, "repeat")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->repeat = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric repeat value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "exp")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->exp = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric exp value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else if (*argv && !strcmp(*argv, "slowfrq")) { |
| if (argv[1] == NULL) { |
| fprintf(stderr, |
| "Missing value for '%s'\n", *argv); |
| return -1; |
| } |
| argv++; |
| statep->slow_freq = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, |
| "Non-numeric slowfrq value %s\n", *argv); |
| return -1; |
| } |
| argv++; |
| } else { |
| /* Not a recognized option, exit inner loop */ |
| break; |
| } |
| } |
| |
| statep->scan_freq = htod32(statep->scan_freq); |
| statep->lost_network_timeout = htod32(statep->lost_network_timeout); |
| statep->flags = htod16(statep->flags); |
| statep->slow_freq = htod32(statep->slow_freq); |
| } |
| |
| /* Should be no leftover args at this point */ |
| if (*argv) { |
| fprintf(stderr, "Input error at %s\n", *argv); |
| return -1; |
| } |
| |
| /* Now do the set */ |
| err = wlu_iovar_setbuf(wl, cmd->name, |
| &mpf_params, sizeof(mpf_params), buf, WLC_IOCTL_MAXLEN); |
| } |
| |
| return err; |
| } |
| |
| static int |
| wl_mpf_map(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_mpf_map_t map; |
| wl_mpf_map_t *mapp; |
| wl_mpf_val_t *valp, *valp2; |
| uint16 mask, val, state, type = 0; |
| uint8 count; |
| char *endptr; |
| uint bitcount; |
| |
| argv++; |
| |
| memset(&map, 0, sizeof(map)); |
| map.version = dtoh16(WL_MPF_VERSION); |
| |
| if (*argv && !strcmp(*argv, "type")) { |
| argv++; |
| if (*argv == NULL) { |
| fprintf(stderr, "No type value specified\n"); |
| return -1; |
| } |
| type = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric type value %s\n", *argv); |
| return -1; |
| } |
| if (type != 0) { |
| fprintf(stderr, "Nonzero type not yet implemented\n"); |
| return -1; |
| } |
| /* If/when nonzero allowed, swap/assign it here */ |
| argv++; |
| } |
| |
| if (*argv == NULL) { |
| /* No arguments beyond type, do a GET and display results */ |
| err = wlu_iovar_getbuf(wl, cmd->name, |
| &map, OFFSETOF(wl_mpf_map_t, mask), buf, WLC_IOCTL_MAXLEN); |
| if (err >= 0) { |
| mapp = (wl_mpf_map_t *)buf; |
| if (dtoh16(mapp->version) != WL_MPF_VERSION) { |
| fprintf(stderr, "Incorrect version (%d != %d).\n", |
| dtoh16(mapp->version), WL_MPF_VERSION); |
| return -1; |
| } |
| printf("Type %d, mask %04x, %d state mappings:\n", |
| dtoh16(mapp->type), dtoh16(mapp->mask), mapp->count); |
| bitcount = bcm_bitcount((uint8*)&mapp->mask, sizeof(mapp->mask)); |
| if (bitcount > WL_MPF_MAX_BITS) { |
| fprintf(stderr, "Invalid mask: more than %d bits\n", |
| WL_MPF_MAX_BITS); |
| return -1; |
| } |
| |
| count = dtoh16(mapp->count); |
| if (!count && !bitcount) { |
| printf("No map configured\n"); |
| } else { |
| if (count != (1 << bitcount)) { |
| fprintf(stderr, "Bit/count mismatch (%d != %d)\n", |
| bitcount, count); |
| return -1; |
| } |
| } |
| |
| for (valp = mapp->vals; count; count--, valp++) { |
| if (valp->name[WL_MPF_STATE_NAME_MAX-1]) { |
| char c = valp->name[WL_MPF_STATE_NAME_MAX-1]; |
| valp->name[WL_MPF_STATE_NAME_MAX-1] = 0; |
| fprintf(stderr, "Invalid name: %s%c\n", |
| valp->name, c); |
| return -1; |
| } |
| |
| printf(" Value: %04x State: %d (%s)\n", |
| dtoh16(valp->val), dtoh16(valp->state), valp->name); |
| } |
| } |
| } else { |
| /* Additional arguments: parse and do a set */ |
| mask = strtoul(*argv, &endptr, 16); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Cannot parse mask %s\n", *argv); |
| return -1; |
| } |
| bitcount = bcm_bitcount((uint8*)&mask, sizeof(mask)); |
| if (bitcount > WL_MPF_MAX_BITS) { |
| fprintf(stderr, "Invalid mask: more than %d bits\n", |
| WL_MPF_MAX_BITS); |
| return -1; |
| } |
| map.mask = htod16(mask); |
| argv++; |
| |
| count = 1 << bitcount; |
| map.count = count; |
| |
| for (valp = map.vals; count && *argv; count--, argv++, valp++) { |
| val = strtoul(*argv, &endptr, 16); |
| if (endptr == *argv || *endptr != '/') { |
| fprintf(stderr, "Invalid value, or missing /state: %s\n", *argv); |
| return -1; |
| } |
| if (val & ~mask) { |
| fprintf(stderr, "Value bits outside mask: %04x/%04x -> %04x\n", |
| val, mask, val & ~mask); |
| return -1; |
| } |
| val = htod16(val); |
| for (valp2 = map.vals; valp2 < valp; valp2++) { |
| if (val == valp2->val) { |
| fprintf(stderr, "Invalid repeated value: %04x\n", |
| dtoh16(val)); |
| return -1; |
| } |
| } |
| valp->val = val; |
| |
| endptr++; |
| if (*endptr == '\0' || *endptr == '/') { |
| fprintf(stderr, "Missing required /<state>: %s\n", *argv); |
| return -1; |
| } |
| state = strtoul(endptr, &endptr, 0); |
| if (*endptr != '\0' && *endptr != '/') { |
| fprintf(stderr, "Non-numeric state value %s\n", endptr); |
| return -1; |
| } |
| if (state >= map.count) { |
| fprintf(stderr, "Invalid state %d, must be 0-%d\n", |
| state, map.count - 1); |
| return -1; |
| } |
| valp->state = htod16(state); |
| |
| if (*endptr++ == '/') { |
| if (strlen(endptr) >= WL_MPF_STATE_NAME_MAX - 1) { |
| fprintf(stderr, "Name %s too long, limit %d chars\n", |
| endptr, WL_MPF_STATE_NAME_MAX - 2); |
| return -1; |
| } |
| if (isdigit(*endptr)) { |
| fprintf(stderr, "Names cannot start with a digit: %s\n", |
| endptr); |
| return -1; |
| } |
| if (!strcmp(endptr, "gpio")) { |
| fprintf(stderr, "Name 'gpio' is reserved\n"); |
| return -1; |
| } |
| strcpy(valp->name, endptr); |
| } |
| } |
| |
| /* Closing overall checks */ |
| if (count) { |
| fprintf(stderr, "Specify all %d possible values, missing %d\n", |
| map.count, count); |
| return -1; |
| } else if (*argv) { |
| fprintf(stderr, "Too many arguments for %d possible states at %s\n", |
| map.count, *argv); |
| return -1; |
| } |
| |
| /* All ok, do the set */ |
| err = wlu_iovar_setbuf(wl, cmd->name, |
| &map, sizeof(map), buf, WLC_IOCTL_MAXLEN); |
| } |
| |
| return err; |
| } |
| |
| static int |
| wl_mpf_state(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| wl_mpf_state_t mpstate; |
| wl_mpf_state_t *mpstatep; |
| uint16 state, type = 0; |
| char *endptr; |
| |
| argv++; |
| |
| memset(&mpstate, 0, sizeof(mpstate)); |
| mpstate.version = dtoh16(WL_MPF_VERSION); |
| |
| if (*argv && !strcmp(*argv, "type")) { |
| argv++; |
| if (*argv == NULL) { |
| fprintf(stderr, "No type value specified\n"); |
| return -1; |
| } |
| type = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric type value %s\n", *argv); |
| return -1; |
| } |
| if (type != 0) { |
| fprintf(stderr, "Nonzero type not yet implemented\n"); |
| return -1; |
| } |
| /* If/when nonzero allowed, swap/assign it here */ |
| argv++; |
| } |
| |
| if (*argv == NULL) { |
| /* No arguments beyond type, do a GET and display results */ |
| err = wlu_iovar_getbuf(wl, cmd->name, |
| &mpstate, OFFSETOF(wl_mpf_state_t, state), |
| buf, WLC_IOCTL_MAXLEN); |
| if (err >= 0) { |
| mpstatep = (wl_mpf_state_t *)buf; |
| if (dtoh16(mpstatep->version) != WL_MPF_VERSION) { |
| fprintf(stderr, "Incorrect version (%d != %d).\n", |
| dtoh16(mpstatep->version), WL_MPF_VERSION); |
| return -1; |
| } |
| if (mpstatep->name[WL_MPF_STATE_NAME_MAX-1]) { |
| fprintf(stderr, "Invalid name\n"); |
| mpstatep->name[WL_MPF_STATE_NAME_MAX-1] = '\0'; |
| } |
| printf("Type %d: state %d, name %s (%s)\n", |
| dtoh16(mpstatep->type), dtoh16(mpstatep->state), |
| (mpstatep->name ? mpstatep->name : "(unknown)"), |
| ((mpstatep->force) ? "forced" : "auto")); |
| } |
| } else { |
| /* Should be one argument, but need to determine what kind */ |
| if (isdigit(**argv)) { |
| state = strtoul(*argv, &endptr, 0); |
| if (*endptr != '\0') { |
| fprintf(stderr, "Non-numeric state: %s\n", *argv); |
| return -1; |
| } |
| mpstate.state = htod16(state); |
| mpstate.force = 1; |
| } else if (!strcmp(*argv, "gpio")) { |
| mpstate.force = 0; |
| } else { |
| if (strlen(*argv) >= WL_MPF_STATE_NAME_MAX-1) { |
| fprintf(stderr, "Name %s too long, limit %d chars\n", |
| *argv, WL_MPF_STATE_NAME_MAX - 2); |
| return -1; |
| } |
| strcpy(mpstate.name, *argv); |
| mpstate.force = 1; |
| } |
| argv++; |
| |
| if (*argv) { |
| fprintf(stderr, "Too many arguments at %s\n", *argv); |
| return -1; |
| } |
| |
| /* All ok, do the set */ |
| err = wlu_iovar_setbuf(wl, cmd->name, |
| &mpstate, sizeof(mpstate), buf, WLC_IOCTL_MAXLEN); |
| } |
| |
| return err; |
| } |
| #endif /* WL_MPF */ |