blob: 1ae5bd01db780f2f5f8bfe516fdd07971a96d020 [file] [log] [blame]
/*
* wl bdo command module - Bonjour Dongle Offload
*
* 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:$
*/
#ifdef WIN32
#include <windows.h>
#endif
#include <wlioctl.h>
#if defined(DONGLEBUILD)
#include <typedefs.h>
#include <osl.h>
#endif
#include <sys/stat.h>
#include <errno.h>
/* 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"
static cmd_func_t wl_bdo;
static cmd_t wl_bdo_cmds[] = {
{ "bdo", wl_bdo, WLC_GET_VAR, WLC_SET_VAR,
"Bonjour dongle offload subcommands:\n"
"\tbdo download <dbase_filename>\n"
"\tbdo enable <0|1>\n"
"\tbdo max_download"},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_bdo_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register bdo commands */
wl_module_cmds_register(wl_bdo_cmds);
}
/* download database from buffer */
static int
wl_bdo_download_database(void *wl, uint8 *database, uint16 len)
{
int err = BCME_OK;
uint16 total_size = len;
uint16 current_size = 0;
uint16 frag_num = 0;
while (current_size < total_size) {
uint8 buffer[OFFSETOF(wl_bdo_t, data) + sizeof(wl_bdo_download_t)];
wl_bdo_t *bdo = (wl_bdo_t *)buffer;
int length;
wl_bdo_download_t *bdo_download = (wl_bdo_download_t *)bdo->data;
/* update size and fragment */
bdo_download->total_size = total_size;
bdo_download->frag_num = frag_num;
bdo_download->frag_size = MIN(total_size - current_size, BDO_MAX_FRAGMENT_SIZE);
memcpy(bdo_download->fragment, &database[current_size], bdo_download->frag_size);
bdo->subcmd_id = WL_BDO_SUBCMD_DOWNLOAD;
bdo->len = OFFSETOF(wl_bdo_download_t, fragment) + bdo_download->frag_size;
/* update before dongle byte order */
current_size += bdo_download->frag_size;
frag_num++;
/* dongle byte order */
bdo_download->total_size = htod16(bdo_download->total_size);
bdo_download->frag_num = htod16(bdo_download->frag_num);
bdo_download->frag_size = htod16(bdo_download->frag_size);
/* invoke iovar */
length = OFFSETOF(wl_bdo_t, data) + bdo->len;
bdo->subcmd_id = htod16(bdo->subcmd_id);
bdo->len = htod16(bdo->len);
if (wlu_iovar_setbuf(wl, "bdo", bdo, length, buf, WLC_IOCTL_MAXLEN) != 0) {
err = BCME_ERROR;
break;
}
}
return err;
}
/* reads file into buffer and returns bytes read and malloc'ed buffer with file contents */
static int
read_file(char *filename, unsigned char **buffer)
{
FILE *fp = NULL;
int ifd;
struct stat filest;
unsigned int filesize = 0;
unsigned long status = 0;
unsigned char *buf = NULL;
/* open the file */
if (!(fp = fopen(filename, "rb"))) {
fprintf(stderr, "unable to open input file %s\n", filename);
goto error;
}
/* get fstat */
ifd = fileno(fp);
if (fstat(ifd, &filest)) {
fprintf(stderr, "fstat on input file %s return error %s\n",
filename, strerror(errno));
goto error;
}
/* get filesize */
filesize = filest.st_size;
if (filesize == 0) {
fprintf(stderr, "input file %s is empty (i.e. zero length)\n", filename);
goto error;
}
/* get buffer */
if ((buf = malloc(filesize)) == NULL) {
fprintf(stderr, "unable to allocate %u bytes based on input file size!\n",
filesize);
goto error;
}
/* read from file to buffer and check length */
status = fread(buf, 1, filesize, fp);
if (status != filesize) {
fprintf(stderr, "read of input file %s wasn't good based on fstat size %u\n",
filename, filesize);
goto error;
}
/* return buffer and filesize */
*buffer = buf;
fclose(fp);
return filesize;
error:
if (buf) {
free(buf);
}
if (fp) {
fclose(fp);
}
return 0;
}
/* converts input hex buffer to binary and returns bytes converted and malloc'ed buffer */
static int
hex2bin(char *input, int len, unsigned char **buffer)
{
unsigned char *buf = NULL;
int num_converted = 0;
char *p;
/* get buffer */
if ((buf = malloc(len)) == NULL) {
fprintf(stderr, "unable to allocate %u bytes\n", len);
return 0;
}
p = input;
while (p < input + len) {
char hex[3];
/* ignore unknown leading/trailing characters */
if (!isprint(*p)) {
p++;
continue;
}
hex[0] = *p++;
hex[1] = *p++;
hex[2] = 0;
/* convert hex to binary */
buf[num_converted++] = strtoul(hex, NULL, 16);
}
/* return buffer and number converted */
*buffer = buf;
return num_converted;
}
/* read database from file and download buffer */
static int
wl_bdo_download_file(void *wl, char *filename)
{
int err;
int file_len;
unsigned char *file_buf;
unsigned char *conv_buf = NULL;
int dload_len;
unsigned char *dload_buf;
if ((file_len = read_file(filename, &file_buf)) == 0) {
return BCME_BADARG;
}
/* conversion to binary is needed if first char is printable */
if (isprint(file_buf[0])) {
dload_len = hex2bin((char *)file_buf, file_len, &conv_buf);
dload_buf = conv_buf;
} else {
dload_len = file_len;
dload_buf = file_buf;
}
err = wl_bdo_download_database(wl, dload_buf, dload_len);
if (err) {
printf("Failed to download. Error code: %d\n", err);
}
else {
printf("%s: %d bytes\n", filename, dload_len);
}
free(file_buf);
if (conv_buf) {
free(conv_buf);
}
return err;
}
static int
wl_bdo(void *wl, cmd_t *cmd, char **argv)
{
int err = -1;
char *subcmd;
int subcmd_len;
/* skip iovar */
argv++;
/* must have subcommand */
subcmd = *argv++;
if (!subcmd) {
return BCME_USAGE_ERROR;
}
subcmd_len = strlen(subcmd);
if (!*argv) {
/* get */
uint8 buffer[OFFSETOF(wl_bdo_t, data)];
wl_bdo_t *bdo = (wl_bdo_t *)buffer;
int len = OFFSETOF(wl_bdo_t, data);
memset(bdo, 0, len);
if (!strncmp(subcmd, "enable", subcmd_len)) {
bdo->subcmd_id = WL_BDO_SUBCMD_ENABLE;
} else if (!strncmp(subcmd, "max_download", subcmd_len)) {
bdo->subcmd_id = WL_BDO_SUBCMD_MAX_DOWNLOAD;
} else {
return BCME_USAGE_ERROR;
}
/* invoke GET iovar */
bdo->subcmd_id = htod16(bdo->subcmd_id);
bdo->len = htod16(bdo->len);
if ((err = wlu_iovar_getbuf(wl, cmd->name, bdo, len, buf, WLC_IOCTL_SMLEN)) < 0) {
return err;
}
/* process and print GET results */
bdo = (wl_bdo_t *)buf;
bdo->subcmd_id = dtoh16(bdo->subcmd_id);
bdo->len = dtoh16(bdo->len);
switch (bdo->subcmd_id) {
case WL_BDO_SUBCMD_ENABLE:
{
wl_bdo_enable_t *bdo_enable = (wl_bdo_enable_t *)bdo->data;
if (bdo->len >= sizeof(*bdo_enable)) {
printf("%d\n", bdo_enable->enable);
} else {
err = BCME_BADLEN;
}
break;
}
case WL_BDO_SUBCMD_MAX_DOWNLOAD:
{
wl_bdo_max_download_t *max_download = (wl_bdo_max_download_t *)bdo->data;
if (bdo->len >= sizeof(*max_download)) {
printf("%d\n", dtoh16(max_download->size));
} else {
err = BCME_BADLEN;
}
break;
}
default:
break;
}
} else if (!strncmp(subcmd, "download", subcmd_len) && argv[0]) {
/* download */
err = wl_bdo_download_file(wl, argv[0]);
}
else {
/* set */
uint8 buffer[OFFSETOF(wl_bdo_t, data) + sizeof(wl_bdo_enable_t)];
wl_bdo_t *bdo = (wl_bdo_t *)buffer;
int len;
if (!strncmp(subcmd, "enable", subcmd_len) &&
(!strcmp(argv[0], "0") || !strcmp(argv[0], "1"))) {
wl_bdo_enable_t *bdo_enable = (wl_bdo_enable_t *)bdo->data;
bdo->subcmd_id = WL_BDO_SUBCMD_ENABLE;
bdo->len = sizeof(*bdo_enable);
bdo_enable->enable = atoi(argv[0]);
} else {
return BCME_USAGE_ERROR;
}
/* invoke SET iovar */
len = OFFSETOF(wl_bdo_t, data) + bdo->len;
bdo->subcmd_id = htod16(bdo->subcmd_id);
bdo->len = htod16(bdo->len);
err = wlu_iovar_set(wl, cmd->name, bdo, len);
}
return err;
}