| /* |
| * wl p2po 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_p2po.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" |
| |
| #include <string.h> |
| |
| #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_p2po_listen; |
| static cmd_func_t wl_p2po_addsvc; |
| static cmd_func_t wl_p2po_delsvc; |
| static cmd_func_t wl_p2po_sd_reqresp; |
| static cmd_func_t wl_p2po_listen_channel; |
| static cmd_func_t wl_p2po_find_config; |
| static int wl_p2po_gas_config(void *wl, cmd_t *cmd, char **argv); |
| static int wl_p2po_wfds_seek_get(void *wl, cmd_t *cmd, char **argv); |
| static int wl_p2po_wfds_seek_add(void *wl, cmd_t *cmd, char **argv); |
| static int wl_p2po_wfds_seek_del(void *wl, cmd_t *cmd, char **argv); |
| static int wl_p2po_wfds_advertise_add(void *wl, cmd_t *cmd, char **argv); |
| static int wl_p2po_wfds_advertise_del(void *wl, cmd_t *cmd, char **argv); |
| #if defined(linux) |
| static cmd_func_t wl_p2po_results; |
| #endif /* linux */ |
| |
| |
| static cmd_t wl_p2po_cmds[] = { |
| { "p2po_listen", wl_p2po_listen, -1, -1, |
| "start/get listen\n" |
| "\n" |
| "\tStart listen\n" |
| "\tusage: p2po_listen <period(ms)> <interval(ms)> [count]\n" |
| "\n" |
| "\tRead back listen period, interval, count\n" |
| "\tusage: p2po_listen" |
| }, |
| { "p2po_find", wl_var_void, -1, WLC_SET_VAR, |
| "start discovery" |
| }, |
| { "p2po_stop", wl_var_void, -1, WLC_SET_VAR, |
| "stop both P2P listen and P2P device discovery offload\n" |
| "\tusage: p2po_stop" |
| }, |
| { "p2po_addsvc", wl_p2po_addsvc, -1, WLC_SET_VAR, |
| "add query-service pair\n" |
| "\tusage: p2po_addsvc <protocol> <\"query\"> <\"response\">\n" |
| "\t\t<protocol>: 1 = Bonjour, 2 = UPnP" |
| }, |
| { "p2po_delsvc", wl_p2po_delsvc, -1, WLC_SET_VAR, |
| "delete query-service pair\n" |
| "\tusage: p2po_delsvc <protocol> <\"query\">\n" |
| "\t\t<protocol>: 1 = Bonjour, 2 = UPnP, 0 = delete all" |
| }, |
| { "p2po_sd_req_resp", wl_p2po_sd_reqresp, -1, WLC_SET_VAR, |
| "find a service\n" |
| "\tusage: p2po_sd_req_resp <protocol> <\"query\">\n" |
| "\t\t<protocol>: 1 = Bonjour, 2 = UPnP" |
| }, |
| { "p2po_sd_cancel", wl_var_void, -1, WLC_SET_VAR, |
| "cancel finding a service" |
| }, |
| { "p2po_listen_channel", wl_p2po_listen_channel, WLC_GET_VAR, WLC_SET_VAR, |
| "set listen channel to channel 1, 6, 11, or default\n" |
| "\tusage: p2po_listen_channel <1|6|11|0>" |
| }, |
| { "p2po_find_config", wl_p2po_find_config, WLC_GET_VAR, WLC_SET_VAR, |
| "set/get the parameters for the p2po_find command\n" |
| "\tusage: p2po_find_config <flags> <home_time> <social channels>\n" |
| "\t flags: bit 0 = scan for both P2P devices and non-P2P APs\n" |
| "\texample: p2po_find_config 0 100 11 165\n" |
| "\t Scan for only P2P devices, home time 100ms, social channels 11 165\n" |
| }, |
| #if defined(linux) |
| { "p2po_results", wl_p2po_results, -1, WLC_SET_VAR, |
| "Listens and displays P2PO results." |
| }, |
| #endif /* linux */ |
| { "p2po_gas_config", wl_p2po_gas_config, WLC_GET_VAR, WLC_SET_VAR, |
| "set GAS state machine tunable parameters\n" |
| "\tusage: p2po_gas_config <max_retrans> <resp_timeout> <max_comeback_delay> <max_retries>" |
| }, |
| { "p2po_wfds_seek_add", wl_p2po_wfds_seek_add, WLC_GET_VAR, WLC_SET_VAR, |
| "\tSet usage: p2po_wfds_seek_add <seek_hdl> <service_hash> <macaddr> <service_name>" |
| " [service_info_req]\n" |
| "Add a WFDS service to seek\n" |
| "\t\t<hdl> An arbitrary number identifying this add request\n" |
| "\t\t<service_hash> 6 hex byte service hash of the service to seek\n" |
| "\t\t<macaddr> 6 hex byte advertiser MAC address to match, all FFs if wildcard\n" |
| "\t\t<service_name> Service name to seek\n" |
| "\t\t<service_info_req> Service Info Request string to send in Service Discovery request\n" |
| "\teg. p2po_wfds_seek_add 1234 1 0x090a0b112233 org.wi-fi.wfds.print.rx\n" |
| "\teg. p2po_wfds_seek_add 1234 1 0x090a0b112233 org.wi-fi.wfds.* abcdefg" |
| "\n" |
| "\tGet usage: p2po_wfds_seek_add <seek_hdl>" |
| "Get information about a configured WFDS seek\n" |
| "\t\t<hdl> The hdl of a previously added WFDS service to seek\n" |
| "\teg. p2po_wfds_seek_add 1234" |
| }, |
| { "p2po_wfds_seek_del", wl_p2po_wfds_seek_del, -1, WLC_SET_VAR, |
| "delete a WFDS service to seek\n" |
| "\tusage: p2po_wfds_seek_del <seek_hdl>\n" |
| "\t\t<hdl> the hdl specified in a previous p2po_wfds_seek_add" |
| }, |
| { "p2po_wfds_seek_dump", wl_var_void, -1, WLC_SET_VAR, |
| "dump WFDS services to seek" |
| }, |
| { "p2po_wfds_advertise_add", wl_p2po_wfds_advertise_add, WLC_GET_VAR, WLC_SET_VAR, |
| "add a WFDS service to advertise\n" |
| "\tSet usage: p2po_wfds_advertise_add <adv_hdl> <adv_id> <cfg_meth>" |
| " <hash> <service_name> <status> [service_info]\n" |
| "\t\t<hdl> An arbitrary number identifying this add request\n" |
| "\t\t<adv_id> 4 hex byte advertisement ID\n" |
| "\t\t<cfg_meth> 2 hex byte WPS config methods supported by this service\n" |
| "\t\t<service_hash> 6 hex byte service hash of the service to advertise\n" |
| "\t\t<service_name> Service name. Text string up to 255 chars\n" |
| "\t\t<status> Status code of the service to advertise, 0...255\n" |
| "\t\t<service_info> Service information to send in Service Discovery Response." |
| " Text string up to 255 chars\n" |
| "\teg. p2po_wfds_advertise_add 4321 0x7a7b9e9f 0x0080 0x1133557799aa" |
| " org.wi-fi.wfds.print.rx 0 abcdefg" |
| "\n" |
| "\tGet usage: p2po_wfds_advertise_add <adv_hdl>" |
| "Get information about a configured WFDS advertise service\n" |
| "\t\t<adv_hdl> The hdl of a previously added WFDS advertise service\n" |
| "\teg. p2po_wfds_advertise_add 4321" |
| }, |
| { "p2po_wfds_advertise_del", wl_p2po_wfds_advertise_del, -1, WLC_SET_VAR, |
| "\tusage: p2po_wfds_advertise_del <adv_hdl>\n" |
| "\t\t<hdl> the hdl specified in a previous p2po_wfds_advertise_add\n" |
| "\teg. p2po_wfds_advertise_del 4321" |
| }, |
| { "p2po_wfds_advertise_dump", wl_var_void, -1, WLC_SET_VAR, |
| "dump WFDS services to advertise" |
| }, |
| { NULL, NULL, 0, 0, NULL } |
| }; |
| |
| static char *buf; |
| |
| /* module initialization */ |
| void |
| wluc_p2po_module_init(void) |
| { |
| /* get the global buf */ |
| buf = wl_get_buf(); |
| |
| /* register p2po commands */ |
| wl_module_cmds_register(wl_p2po_cmds); |
| } |
| |
| #define UPNP_QUERY_VERSION "0x10" |
| #define BCM_SVC_RPOTYPE_ALL 0 |
| #define BCM_SVC_RPOTYPE_BONJOUR 1 |
| #define BCM_SVC_RPOTYPE_UPNP 2 |
| #define BCM_SVC_RPOTYPE_WSD 3 |
| #define BCM_SVC_RPOTYPE_VENDOR 255 |
| |
| static int |
| wl_p2po_listen(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err, params_size; |
| wl_p2po_listen_t params; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (!argv[1]) { |
| if ((err = wlu_iovar_get(wl, cmd->name, (void *) ¶ms, |
| (sizeof(wl_p2po_listen_t)))) < 0) |
| return (err); |
| printf(" listen period=%u listen interval=%u listen count=%u\n", |
| dtoh16(params.period), dtoh16(params.interval), dtoh16(params.count)); |
| return BCME_OK; |
| } |
| |
| if (!argv[2] || !argv[3]) |
| return BCME_USAGE_ERROR; |
| |
| params.period = atoi(argv[1]); |
| params.interval = atoi(argv[2]); |
| params.count = atoi(argv[3]); |
| |
| params_size = sizeof(wl_p2po_listen_t); |
| |
| err = wlu_iovar_set(wl, "p2po_listen", ¶ms, params_size); |
| |
| return err; |
| } |
| |
| static int |
| wl_p2po_addsvc(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| int protocol, query_len, response_len, params_size; |
| char *query, *response; |
| wl_p2po_qr_t *params; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (!argv[1] || !argv[2] || !argv[3]) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| protocol = atoi(argv[1]); |
| if (protocol == BCM_SVC_RPOTYPE_BONJOUR) { |
| query = argv[2]; |
| response = argv[3]; |
| query_len = hexstr2hex(query); |
| response_len = hexstr2hex(response); |
| } else if (protocol == BCM_SVC_RPOTYPE_UPNP) { |
| if (memcmp(argv[2], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0 || |
| memcmp(argv[3], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) { |
| fprintf(stderr, "UPnP query/response string must start with 0x10"); |
| return BCME_USAGE_ERROR; |
| } |
| query = argv[2] + strlen(UPNP_QUERY_VERSION) - 1; |
| response = argv[3] + strlen(UPNP_QUERY_VERSION) - 1; |
| query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16); |
| response[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16); |
| |
| query_len = strlen(query); |
| response_len = strlen(response); |
| } |
| else { |
| fprintf(stderr, "<protocol> should be <1|2>\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| params_size = sizeof(wl_p2po_qr_t) + query_len + response_len - 1; |
| params = (wl_p2po_qr_t *)malloc(params_size); |
| if (params == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| |
| params->protocol = protocol; |
| params->query_len = query_len; |
| params->response_len = response_len; |
| memcpy(params->qrbuf, query, query_len); |
| memcpy(params->qrbuf+query_len, response, response_len); |
| |
| err = wlu_iovar_setbuf(wl, "p2po_addsvc", params, params_size, buf, WLC_IOCTL_MAXLEN); |
| |
| free(params); |
| |
| return err; |
| } |
| |
| static int |
| wl_p2po_delsvc(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| int protocol, query_len, params_size; |
| char *query = NULL; |
| wl_p2po_qr_t *params; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (!argv[1] || (*argv[1] != '0' && !argv[2])) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (*argv[1] == '0') { |
| protocol = 0; |
| query_len = 0; |
| } |
| else { |
| protocol = atoi(argv[1]); |
| if (protocol == BCM_SVC_RPOTYPE_BONJOUR) { |
| query = argv[2]; |
| query_len = hexstr2hex(query); |
| } else if (protocol == BCM_SVC_RPOTYPE_UPNP) { |
| if (memcmp(argv[2], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) { |
| fprintf(stderr, "UPnP query string must start with 0x10"); |
| return BCME_USAGE_ERROR; |
| } |
| query = argv[2] + strlen(UPNP_QUERY_VERSION) - 1; |
| query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16); |
| |
| query_len = strlen(query); |
| } |
| else { |
| fprintf(stderr, "<protocol> should be <1|2>\n"); |
| return BCME_USAGE_ERROR; |
| } |
| } |
| |
| params_size = sizeof(wl_p2po_qr_t) + (query_len ? query_len - 1 : 0); |
| params = (wl_p2po_qr_t *)malloc(params_size); |
| if (params == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| |
| params->protocol = protocol; |
| params->query_len = query_len; |
| if (query_len) |
| memcpy(params->qrbuf, query, query_len); |
| |
| err = wlu_iovar_set(wl, "p2po_delsvc", params, params_size); |
| |
| free(params); |
| |
| return err; |
| } |
| |
| static int |
| wl_p2po_sd_reqresp(void *wl, cmd_t *cmd, char **argv) |
| { |
| int err; |
| int transaction_id, protocol, query_len, params_size; |
| char *query = NULL; |
| wl_p2po_qr_t *params; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (!argv[1] || !argv[2]) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| transaction_id = atoi(argv[1]); |
| if (transaction_id < 0 || transaction_id > 255) { |
| fprintf(stderr, "<transaction id> should be between 0 and 255\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| protocol = atoi(argv[2]); |
| if (!argv[3]) { |
| /* find everything for protocol */ |
| query_len = 0; |
| } else if (protocol == BCM_SVC_RPOTYPE_BONJOUR) { |
| query = argv[3]; |
| query_len = hexstr2hex(query); |
| } else if (protocol == BCM_SVC_RPOTYPE_UPNP) { |
| if (memcmp(argv[3], UPNP_QUERY_VERSION, strlen(UPNP_QUERY_VERSION)) != 0) { |
| fprintf(stderr, "UPnP query string must start with 0x10"); |
| return BCME_USAGE_ERROR; |
| } |
| query = argv[3] + strlen(UPNP_QUERY_VERSION) - 1; |
| query[0] = strtoul(UPNP_QUERY_VERSION, NULL, 16); |
| |
| query_len = strlen(query); |
| } else { |
| fprintf(stderr, "<protocol> should be <0|1|2>\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| params_size = sizeof(wl_p2po_qr_t) + (query_len ? query_len - 1 : 0); |
| params = (wl_p2po_qr_t *)malloc(params_size); |
| if (params == NULL) { |
| fprintf(stderr, "Not enough memory\n"); |
| return BCME_NOMEM; |
| } |
| |
| params->transaction_id = transaction_id; |
| params->protocol = protocol; |
| params->query_len = query_len; |
| if (query_len) |
| memcpy(params->qrbuf, query, query_len); |
| |
| err = wlu_iovar_set(wl, "p2po_sd_req_resp", params, params_size); |
| |
| free(params); |
| |
| return err; |
| } |
| |
| static int |
| wl_p2po_listen_channel(void *wl, cmd_t *cmd, char **argv) |
| { |
| int error; |
| int32 val; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| if (*++argv) { |
| val = atoi(*argv); |
| return wlu_iovar_setint(wl, "p2po_listen_channel", val); |
| } else { |
| error = wlu_iovar_getint(wl, "p2po_listen_channel", &val); |
| if (error < 0) |
| return error; |
| printf("P2P Offload Listen Channel: %d\n", dtoh32(val)); |
| return BCME_OK; |
| } |
| } |
| |
| static int |
| wl_p2po_find_config(void *wl, cmd_t *cmd, char **argv) |
| { |
| uint argc; |
| int error; |
| int i; |
| wl_p2po_find_config_t *fc; |
| |
| UNUSED_PARAMETER(cmd); |
| |
| /* Get arg count */ |
| for (argc = 0; argv[argc]; argc++) |
| ; |
| |
| if (argc == 0) |
| return BCME_ERROR; |
| |
| if (argc == 1) { |
| error = wlu_iovar_getbuf(wl, cmd->name, NULL, 0, buf, WLC_IOCTL_MEDLEN); |
| if (error < 0) |
| return error; |
| |
| fc = (wl_p2po_find_config_t*) buf; |
| printf("P2P Offload Find configuration:\n"); |
| printf(" Version: %u Length: %u\n", |
| dtoh16(fc->version), dtoh16(fc->length)); |
| printf(" Scan for non-P2P APs: %d\n", |
| (fc->flags & P2PO_FIND_FLAG_SCAN_ALL_APS) != 0); |
| printf(" Search home time: %d\n", dtoh32(fc->search_home_time)); |
| printf(" # of social channels: %u\n", fc->num_social_channels); |
| printf(" Social channels:"); |
| for (i = 0; i < fc->num_social_channels; i++) { |
| printf(" %u", fc->social_channels[i]); |
| } |
| printf("\n"); |
| } else if (argc <= 3) { |
| fprintf(stderr, "At least 1 social channel is required.\n"); |
| error = BCME_BADARG; |
| } else { |
| uint fc_size; |
| uint16 channel; |
| uint j; |
| |
| fc_size = sizeof(wl_p2po_find_config_t) + (argc - 2) * sizeof(uint16); |
| fc = (wl_p2po_find_config_t*) malloc(fc_size); |
| if (fc == NULL) { |
| printf("Cannot not allocate %d bytes for p2po_find_config buffer\n", |
| fc_size); |
| return BCME_NOMEM; |
| } |
| memset(fc, 0, fc_size); |
| |
| fc->version = WL_P2PO_FIND_CONFIG_VERSION; |
| fc->length = sizeof(*fc); |
| fc->flags = atoi(argv[1]); |
| fc->search_home_time = atoi(argv[2]); |
| |
| /* Set the social channel list */ |
| i = 0; |
| for (j = 3; j < argc; j++, i++) { |
| channel = atoi(argv[j]); |
| if (channel == 0) { |
| printf("Invalid channel %s\n", argv[j]); |
| break; |
| } else { |
| fc->social_channels[i] = channel; |
| } |
| } |
| fc->num_social_channels = i; |
| |
| error = wlu_iovar_setbuf(wl, cmd->name, fc, fc_size, buf, |
| WLC_IOCTL_MEDLEN); |
| free(fc); |
| } |
| |
| return error; |
| } |
| |
| #if defined(linux) |
| #define P2PO_EVENTS_BUFFER_SIZE 2048 |
| |
| static int |
| wl_p2po_results(void *wl, cmd_t *cmd, char **argv) |
| { |
| int fd, err, octets; |
| struct sockaddr_ll sll; |
| struct ifreq ifr; |
| char *data; |
| uint8 event_inds_mask[WL_EVENTING_MASK_LEN]; /* event bit mask */ |
| |
| UNUSED_PARAMETER(cmd); |
| UNUSED_PARAMETER(argv); |
| |
| 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); |
| event_inds_mask[WLC_E_SERVICE_FOUND / 8] |= 1 << (WLC_E_SERVICE_FOUND % 8); |
| event_inds_mask[WLC_E_GAS_FRAGMENT_RX / 8] |= 1 << (WLC_E_GAS_FRAGMENT_RX % 8); |
| if ((err = wlu_iovar_set(wl, "event_msgs", &event_inds_mask, WL_EVENTING_MASK_LEN))) |
| goto exit2; |
| |
| fd = socket(PF_PACKET, SOCK_RAW, hton16(ETHER_TYPE_BRCM)); |
| if (fd < 0) { |
| printf("Cannot create socket %d\n", fd); |
| err = -1; |
| goto exit2; |
| } |
| |
| err = ioctl(fd, SIOCGIFINDEX, &ifr); |
| if (err < 0) { |
| printf("Cannot get index %d\n", err); |
| goto exit1; |
| } |
| |
| /* bind the socket first before starting so we won't miss any event */ |
| memset(&sll, 0, sizeof(sll)); |
| sll.sll_family = AF_PACKET; |
| sll.sll_protocol = hton16(ETHER_TYPE_BRCM); |
| sll.sll_ifindex = ifr.ifr_ifindex; |
| err = bind(fd, (struct sockaddr *)&sll, sizeof(sll)); |
| if (err < 0) { |
| printf("Cannot bind %d\n", err); |
| goto exit1; |
| } |
| |
| data = (char*)malloc(P2PO_EVENTS_BUFFER_SIZE); |
| |
| if (data == NULL) { |
| printf("Cannot not allocate %d bytes for events receive buffer\n", |
| P2PO_EVENTS_BUFFER_SIZE); |
| err = BCME_NOMEM; |
| goto exit1; |
| } |
| |
| /* receive result */ |
| while (1) { |
| bcm_event_t *bcm_event; |
| int event_type; |
| |
| octets = recv(fd, data, P2PO_EVENTS_BUFFER_SIZE, 0); |
| bcm_event = (bcm_event_t *)data; |
| event_type = ntoh32(bcm_event->event.event_type); |
| |
| if (octets >= (int)sizeof(bcm_event_t)) { |
| if (event_type == WLC_E_SERVICE_FOUND) { |
| uint32 status = ntoh32(bcm_event->event.status); |
| wl_event_sd_t *sd_data = |
| (wl_event_sd_t *)&data[sizeof(bcm_event_t)]; |
| |
| sd_data->channel = dtoh16(sd_data->channel); |
| |
| printf("WLC_E_SERVICE_FOUND: %s\n", |
| status == WLC_E_STATUS_PARTIAL ? "WLC_E_STATUS_PARTIAL" : |
| status == WLC_E_STATUS_SUCCESS ? "WLC_E_STATUS_SUCCESS" : |
| status == WLC_E_STATUS_FAIL ? "WLC_E_STATUS_FAIL" : |
| "unknown"); |
| printf(" channel = %d\n", sd_data->channel); |
| printf(" peer = %s\n", |
| wl_ether_etoa(&bcm_event->event.addr)); |
| if (status == WLC_E_STATUS_SUCCESS) { |
| wl_sd_tlv_t *tlv; |
| int i; |
| |
| tlv = sd_data->tlv; |
| for (i = 0; i < sd_data->count; i++) { |
| printf(" [TLV %d]\n", i); |
| tlv->length = dtoh16(tlv->length); |
| printf(" protocol type = %s\n", |
| tlv->protocol == 0 ? "ALL" : |
| tlv->protocol == BCM_SVC_RPOTYPE_BONJOUR ? |
| "BONJOUR" : |
| tlv->protocol == BCM_SVC_RPOTYPE_UPNP ? |
| "UPnP" : |
| tlv->protocol == BCM_SVC_RPOTYPE_WSD ? |
| "WS-Discovery" : |
| tlv->protocol == BCM_SVC_RPOTYPE_VENDOR ? |
| "Vendor specific" : "Unknown"); |
| printf(" transaction id = %u\n", |
| tlv->transaction_id); |
| printf(" status code = %u\n", |
| tlv->status_code); |
| printf(" length = %u\n", tlv->length); |
| wl_hexdump(tlv->data, tlv->length); |
| |
| tlv = (wl_sd_tlv_t *)((void *)tlv + tlv->length + |
| OFFSETOF(wl_sd_tlv_t, data)); |
| } |
| } |
| else { |
| /* fragments */ |
| } |
| } |
| } |
| } |
| |
| free(data); |
| exit1: |
| close(fd); |
| exit2: |
| return err; |
| } |
| #endif /* linux */ |
| |
| static int |
| wl_p2po_gas_config(void *wl, cmd_t *cmd, char **argv) |
| { |
| int error; |
| wl_gas_config_t set; |
| wl_gas_config_t* get; |
| |
| if (!argv[1]) { |
| error = wlu_iovar_getbuf(wl, "p2po_gas_config", &set, sizeof(set), |
| buf, WLC_IOCTL_MAXLEN); |
| if (error < 0) |
| return error; |
| get = (wl_gas_config_t*) buf; |
| printf("P2P Offload GAS state machine tunable parameters:\n"); |
| printf(" max_retrans=%u resp_timeout=%u\n", |
| dtoh16(get->max_retransmit), dtoh16(get->response_timeout)); |
| printf(" max_comeback_delay=%u max_retries=%u\n", |
| dtoh16(get->max_comeback_delay), dtoh16(get->max_retries)); |
| return BCME_OK; |
| } |
| |
| if (!argv[1] || !argv[2] || !argv[3] || !argv[4]) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| set.max_retransmit = htod16(atoi(argv[1])); |
| set.response_timeout = htod16(atoi(argv[2])); |
| set.max_comeback_delay = htod16(atoi(argv[3])); |
| set.max_retries = htod16(atoi(argv[4])); |
| |
| return wlu_iovar_setbuf(wl, cmd->name, &set, sizeof(set), buf, WLC_IOCTL_MEDLEN); |
| } |
| |
| void |
| wl_p2po_print_name(char *prefix, uint namelen, uint8 *name) |
| { |
| char namez[32 + 1]; |
| |
| if (namelen > sizeof(namez) - 1) |
| namelen = sizeof(namez) - 1; |
| memcpy(namez, name, namelen); |
| namez[namelen] = '\0'; |
| |
| printf("%s%u,%s\n", prefix, namelen, namez); |
| } |
| |
| static int |
| wl_p2po_wfds_seek_get(void *wl, cmd_t *cmd, char **argv) |
| { |
| wl_p2po_wfds_seek_add_t* seek = (wl_p2po_wfds_seek_add_t*) buf; |
| wl_p2po_wfds_seek_add_t seek0; |
| int ret; |
| |
| if (!argv[1]) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| memset(&seek0, 0, sizeof(seek0)); |
| seek0.seek_hdl = atoi(argv[1]); |
| |
| ret = wlu_iovar_getbuf(wl, cmd->name, &seek0, sizeof(seek0), seek, WLC_IOCTL_MAXLEN); |
| if (ret < 0) { |
| goto exit; |
| } |
| |
| printf("Seek service hdl=%u\n", seek->seek_hdl); |
| printf(" hash=0x%02x%02x%02x%02x%02x%02x\n", |
| seek->service_hash[0], seek->service_hash[1], seek->service_hash[2], |
| seek->service_hash[3], seek->service_hash[4], seek->service_hash[5]); |
| printf(" mac=%02x:%02x:%02x:%02x:%02x:%02x\n", |
| seek->addr[0], seek->addr[1], seek->addr[2], |
| seek->addr[3], seek->addr[4], seek->addr[5]); |
| printf(" name length=%u\n", seek->service_name_len); |
| printf(" name=%s\n", seek->service_name); |
| printf(" info_req length=%u\n", seek->service_info_req_len); |
| printf(" info_req=%s\n", seek->service_info_req); |
| |
| exit: |
| return ret; |
| } |
| |
| static int |
| wl_p2po_wfds_seek_add(void *wl, cmd_t *cmd, char **argv) |
| { |
| int malloc_size; |
| wl_p2po_wfds_seek_add_t* seek; |
| int len; |
| int ret; |
| int iovbuf_size; |
| uint8 *iovbuf; |
| |
| /* If only a seek_hdl parameter is given, read an existing seek */ |
| if (!argv[2]) { |
| return wl_p2po_wfds_seek_get(wl, cmd, argv); |
| } |
| |
| if (!argv[4]) { |
| fprintf(stderr, "Too few arguments for iovar SET\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| malloc_size = sizeof(*seek) + MAX_WFDS_SEEK_SVC_INFO_LEN - 1; |
| seek = malloc(malloc_size); |
| if (!seek) { |
| fprintf(stderr, "malloc error, size=%d\n", malloc_size); |
| return BCME_NOMEM; |
| } |
| memset(seek, 0, malloc_size); |
| |
| /* Get <seek_hdl> argument */ |
| seek->seek_hdl = atoi(argv[1]); |
| |
| /* Get <service_hash> argument */ |
| if (argv[2]) { |
| if (strlen(argv[2]) != 14) { |
| fprintf(stderr, "bad argument: %s\n", argv[2]); |
| fprintf(stderr, "<svc_hash> must be 6 hex bytes, eg. '0x112233aabbcc'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| len = wl_pattern_atoh(argv[2], (char*)seek->service_hash); |
| if (len != 6) { |
| fprintf(stderr, "invalid hex string: %s\n", argv[2]); |
| fprintf(stderr, "<svc_hash> must be 6 hex bytes, eg. '0x112233aabbcc'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| } |
| |
| /* Get <macaddr> argument */ |
| if (!wl_ether_atoe(argv[3], (struct ether_addr*) seek->addr)) { |
| fprintf(stderr, "invalid MAC address: %s\n", argv[2]); |
| fprintf(stderr, "<macaddr> must be like '00:90:4c:7a:8b:fe'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| /* Get <service_name> argument */ |
| if (argv[4]) { |
| len = strlen(argv[4]); |
| memcpy(seek->service_name, argv[4], len); |
| seek->service_name_len = len; |
| } else { |
| memcpy(seek->service_name, "org.wi-fi.wfds", strlen("org.wi-fi.wfds")); |
| seek->service_name_len = strlen((char*)seek->service_name); |
| } |
| wl_p2po_print_name("svc_name: ", seek->service_name_len, seek->service_name); |
| |
| /* Get optional [service_info] argument */ |
| if (argv[5]) { |
| len = strlen(argv[5]); |
| if (len > MAX_WFDS_SEEK_SVC_INFO_LEN) |
| len = MAX_WFDS_SEEK_SVC_INFO_LEN; |
| memcpy(seek->service_info_req, argv[5], len); |
| seek->service_info_req_len = len; |
| } else { |
| seek->service_info_req_len = 0; |
| } |
| wl_p2po_print_name("svc_info_req: ", seek->service_info_req_len, |
| seek->service_info_req); |
| |
| iovbuf_size = malloc_size + strlen(cmd->name) + 1; |
| iovbuf = malloc(iovbuf_size); |
| if (!iovbuf) { |
| fprintf(stderr, "malloc error, size=%d\n", iovbuf_size); |
| ret = BCME_NOMEM; |
| goto exit; |
| } |
| ret = wlu_iovar_setbuf(wl, cmd->name, seek, malloc_size, iovbuf, iovbuf_size); |
| free(iovbuf); |
| if (ret != BCME_OK) |
| fprintf(stderr, "%s failed: %d\n", cmd->name, ret); |
| exit: |
| free(seek); |
| return ret; |
| } |
| |
| static int |
| wl_p2po_wfds_seek_del(void *wl, cmd_t *cmd, char **argv) |
| { |
| wl_p2po_wfds_seek_del_t* del; |
| int ret; |
| |
| if (!argv[1]) { |
| fprintf(stderr, "No seek_hdl is specified\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| del = malloc(sizeof(*del)); |
| if (!del) { |
| fprintf(stderr, "malloc error, size=%u\n", (uint)sizeof(*del)); |
| return BCME_NOMEM; |
| } |
| |
| del->seek_hdl = atoi(argv[1]); |
| ret = wlu_iovar_setbuf(wl, cmd->name, del, sizeof(*del), buf, WLC_IOCTL_MAXLEN); |
| |
| free(del); |
| return ret; |
| } |
| |
| static int |
| wl_p2po_wfds_advertise_get(void *wl, cmd_t *cmd, char **argv) |
| { |
| int malloc_size; |
| wl_p2po_wfds_advertise_add_t* add; |
| wl_p2po_wfds_advertise_add_t add0; |
| uint16 svc_info_len; |
| int ret; |
| |
| if (!argv[1]) { |
| fprintf(stderr, "Too few arguments\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| memset(&add0, 0, sizeof(add0)); |
| add0.advertise_hdl = atoi(argv[1]); |
| svc_info_len = MAX_WFDS_ADV_SVC_INFO_LEN; |
| |
| /* Allocate enough memory to hold the full advertisement data */ |
| malloc_size = sizeof(*add) + svc_info_len + strlen(cmd->name) + 1; |
| add = malloc(malloc_size); |
| if (!add) { |
| fprintf(stderr, "malloc error, size=%d\n", malloc_size); |
| ret = BCME_NOMEM; |
| goto exit; |
| } |
| |
| ret = wlu_iovar_getbuf(wl, cmd->name, &add0, sizeof(add0), add, malloc_size); |
| if (ret < 0) { |
| goto exit; |
| } |
| |
| /* Print the retrieved advertisement data */ |
| printf("Advertise service hdl=%u\n", add->advertise_hdl); |
| printf(" hash=0x%02x%02x%02x%02x%02x%02x\n", |
| add->service_hash[0], add->service_hash[1], add->service_hash[2], |
| add->service_hash[3], add->service_hash[4], add->service_hash[5]); |
| printf(" advertisement_id=0x%x\n", add->advertisement_id); |
| printf(" config_method=0x%x\n", add->service_config_method); |
| printf(" name length=%u\n", add->service_name_len); |
| printf(" name=%s\n", add->service_name); |
| printf(" status=%u\n", add->service_status); |
| printf(" info length=%u\n", add->service_info_len); |
| printf(" info=%s\n", add->service_info); |
| goto exit; |
| |
| exit: |
| free(add); |
| return ret; |
| } |
| |
| static int |
| wl_p2po_wfds_advertise_add(void *wl, cmd_t *cmd, char **argv) |
| { |
| int malloc_size; |
| wl_p2po_wfds_advertise_add_t* add = NULL; |
| uint16 svc_info_len = 0; |
| int gen_data_len = 0; |
| int len; |
| int ret; |
| int iovbuf_size; |
| uint8 *iovbuf; |
| |
| /* If only an adv_hdl parameter is given, read an existing advertisement */ |
| if (!argv[2]) { |
| return wl_p2po_wfds_advertise_get(wl, cmd, argv); |
| } |
| |
| if (!argv[6]) { |
| fprintf(stderr, "Too few arguments for iovar SET\n"); |
| return BCME_USAGE_ERROR; |
| } |
| |
| if (argv[7]) { |
| svc_info_len = strlen(argv[7]); |
| if (svc_info_len > 7 && memcmp(argv[7], "gendata", 7) == 0) { |
| gen_data_len = atoi((char*)(argv[7] + 7)); |
| if (gen_data_len > MAX_WFDS_ADV_SVC_INFO_LEN) { |
| fprintf(stderr, "gendata length %d too long, max is %d\n", |
| gen_data_len, MAX_WFDS_ADV_SVC_INFO_LEN); |
| return BCME_USAGE_ERROR; |
| } |
| svc_info_len = gen_data_len; |
| } |
| } |
| |
| malloc_size = sizeof(*add) + svc_info_len; |
| add = malloc(malloc_size); |
| if (!add) { |
| fprintf(stderr, "malloc error, size=%d\n", malloc_size); |
| return BCME_NOMEM; |
| } |
| |
| add->advertise_hdl = atoi(argv[1]); |
| |
| if (strlen(argv[2]) != 10) { |
| fprintf(stderr, "<adv_id> must be 4 hex bytes, eg. '0x8899aabb'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| len = wl_pattern_atoh(argv[2], (char*)&add->advertisement_id); |
| if (len != 4) { |
| fprintf(stderr, "<adv_id> must be 4 hex bytes, eg. '0x8899aabb'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (strlen(argv[3]) != 6) { |
| fprintf(stderr, "<cfg_meth> must be 2 hex bytes, eg. '0x0080'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| len = wl_pattern_atoh(argv[3], (char*)&add->service_config_method); |
| if (len != 2) { |
| fprintf(stderr, "<cfg_meth> must be 2 hex bytes, eg. '0x0080'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| if (strlen(argv[4]) != 14) { |
| fprintf(stderr, "Bad <hash> parameter: %s\n", argv[4]); |
| fprintf(stderr, "<hash> must be 6 hex bytes, eg. '0x1133557799aa'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| len = wl_pattern_atoh(argv[4], (char*)&add->service_hash); |
| if (len != 6) { |
| fprintf(stderr, "Incorrect <hash> parameter: %s\n", argv[4]); |
| fprintf(stderr, "<hash> must be 6 hex bytes, eg. '0x1133557799aa'\n"); |
| ret = BCME_USAGE_ERROR; |
| goto exit; |
| } |
| |
| add->service_name_len = strlen(argv[5]); |
| memcpy(add->service_name, argv[5], add->service_name_len); |
| |
| add->service_status = atoi(argv[6]); |
| |
| if (svc_info_len) { |
| add->service_info_len = svc_info_len; |
| if (add->service_info_len > MAX_WFDS_ADV_SVC_INFO_LEN) |
| add->service_info_len = MAX_WFDS_ADV_SVC_INFO_LEN; |
| if (gen_data_len == 0) |
| memcpy(add->service_info, argv[7], add->service_info_len); |
| } else { |
| add->service_info_len = 0; |
| } |
| |
| |
| /* If the service info consists of a magic string of the form |
| * "gendata<N>" then generate N bytes of service info data. |
| * eg. "gendata2048" will generate a 2048 byte service info string |
| * consisting of the repeated string "0123456789". |
| */ |
| if (gen_data_len > 0) { |
| int i; |
| |
| fprintf(stderr, "Generating %u bytes of service info data\n", |
| add->service_info_len); |
| for (i = 0; i < add->service_info_len; i++) { |
| add->service_info[i] = '0' + (i % 10); |
| } |
| } |
| |
| iovbuf_size = malloc_size + strlen(cmd->name) + 1; |
| iovbuf = malloc(iovbuf_size); |
| if (!iovbuf) { |
| fprintf(stderr, "malloc error, size=%d\n", iovbuf_size); |
| ret = BCME_NOMEM; |
| goto exit; |
| } |
| ret = wlu_iovar_setbuf(wl, cmd->name, add, malloc_size, iovbuf, iovbuf_size); |
| free(iovbuf); |
| if (ret != BCME_OK) |
| fprintf(stderr, "%s failed: %d\n", cmd->name, ret); |
| exit: |
| free(add); |
| return ret; |
| } |
| |
| static int |
| wl_p2po_wfds_advertise_del(void *wl, cmd_t *cmd, char **argv) |
| { |
| wl_p2po_wfds_advertise_del_t* del; |
| unsigned int malloc_size = sizeof(*del); |
| int ret; |
| |
| del = malloc(malloc_size); |
| if (!del) { |
| fprintf(stderr, "malloc error, size=%u\n", malloc_size); |
| return BCME_NOMEM; |
| } |
| |
| if (!argv[1]) { |
| fprintf(stderr, "No advertise_hdl is specified\n"); |
| free(del); |
| return BCME_USAGE_ERROR; |
| } |
| |
| del->advertise_hdl = atoi(argv[1]); |
| ret = wlu_iovar_setbuf(wl, cmd->name, del, sizeof(*del), buf, WLC_IOCTL_MAXLEN); |
| |
| free(del); |
| return ret; |
| } |