blob: 6fc7f988a1bf66b1314a0a935ed9e028b4bf33e8 [file] [log] [blame]
/*
* 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 *) &params,
(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", &params, 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;
}