blob: df36f28718b80b8f4f962eddc6d5fcf1c656a464 [file] [log] [blame]
/*
* wl bmac 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_bmac.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 <bcmsrom_fmt.h>
#include <bcmsrom_tbl.h>
/* For backwards compatibility, the absense of the define 'NO_FILESYSTEM_SUPPORT'
* implies that a filesystem is supported.
*/
#if !defined(BWL_NO_FILESYSTEM_SUPPORT)
#define BWL_FILESYSTEM_SUPPORT
#endif
static cmd_func_t wl_gpioout;
static cmd_func_t wl_nvsource;
static cmd_func_t wl_var_getinthex;
static cmd_func_t wl_otpw, wl_otpraw;
static cmd_func_t wl_devpath;
static cmd_func_t wl_diag;
static cmd_func_t wl_var_setintandprintstr;
static cmd_func_t wl_otpdump_iter;
static cmd_func_t wlu_srwrite_data;
static cmd_t wl_bmac_cmds[] = {
{ "srcrc", wlu_srwrite, WLC_GET_SROM, -1,
"Get the CRC for input binary file" },
{ "cis_source", wl_varint, WLC_GET_VAR, -1,
"Display which source is used for the SDIO CIS"},
{ "nvram_source", wl_nvsource, WLC_GET_VAR, -1,
"Display which source is used for nvram"},
{ "customvar1", wl_var_getinthex, -1, -1,
"print the value of customvar1 in hex format" },
{ "gpioout", wl_gpioout, -1, -1,
"Set any GPIO pins to any value. Use with caution as GPIOs would be "
"assigned to chipcommon\n"
"\tUsage: gpiomask gpioval"},
{ "devpath", wl_devpath, WLC_GET_VAR, -1,
"print device path" },
{ "otpraw", wl_otpraw, WLC_GET_VAR, WLC_SET_VAR,
"Read/Write raw data to on-chip otp\n"
"Usage: wl otpraw <offset> <bits> [<data>]"},
{ "otpw", wl_otpw, -1, WLC_OTPW,
"Write an srom image to on-chip otp\n"
"Usage: wl otpw file"},
{ "nvotpw", wl_otpw, -1, WLC_NVOTPW,
"Write nvram to on-chip otp\n"
"Usage: wl nvotpw file"},
{ "diag", wl_diag, WLC_GET_VAR, -1,
"diag testindex(1-interrupt, 2-loopback, 3-memory, 4-led);"
" precede by 'wl down' and follow by 'wl up'" },
{ "otpdump", wl_otpdump_iter, WLC_GET_VAR, -1,
"Dump raw otp"},
{ "otpstat", wl_var_setintandprintstr, WLC_GET_VAR, -1,
"Dump OTP status"},
{ "srwrite_data", wlu_srwrite_data, WLC_GET_SROM, WLC_SET_SROM,
"Write caldata to srom: srwrite_data -t type filename\n"
"\t Supported types: calblob"},
{ NULL, NULL, 0, 0, NULL }
};
static char *buf;
/* module initialization */
void
wluc_bmac_module_init(void)
{
/* get the global buf */
buf = wl_get_buf();
/* register bmac commands */
wl_module_cmds_register(wl_bmac_cmds);
}
static int
wl_gpioout(void *wl, cmd_t *cmd, char **argv)
{
uint32 mask;
uint32 val;
char *endptr = NULL;
uint argc;
uint32 *int_ptr;
UNUSED_PARAMETER(cmd);
val = 0;
/* eat command name */
argv++;
/* arg count */
for (argc = 0; argv[argc]; argc++)
;
/* Get and print the values */
if (argc == 0) {
uint32 gpio_cntrl;
uint32 gpio_out;
uint32 gpio_outen;
int ret;
if ((ret = wlu_iovar_get(wl, "gpioout", buf, sizeof(uint32) *3)) < 0)
return ret;
gpio_cntrl = dtoh32(((uint32 *)buf)[0]);
gpio_out = dtoh32(((uint32 *)buf)[1]);
gpio_outen = dtoh32(((uint32 *)buf)[2]);
printf("gpiocontrol 0x%x gpioout 0x%x gpioouten 0x%x\n", gpio_cntrl,
gpio_out, gpio_outen);
return 0;
}
/* required arg: mask value */
if (argc < 2)
return BCME_USAGE_ERROR;
mask = strtoul(argv[0], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
val = strtoul(argv[1], &endptr, 0);
if (*endptr != '\0')
return BCME_USAGE_ERROR;
if ((~mask & val) != 0)
return BCME_BADARG;
int_ptr = (uint32 *)buf;
mask = htod32(mask);
memcpy(int_ptr, (const void *)&mask, sizeof(mask));
int_ptr++;
val = htod32(val);
memcpy(int_ptr, (const void *)&val, sizeof(val));
return wlu_iovar_set(wl, "gpioout", buf, sizeof(uint32) *2);
}
static int
wl_nvsource(void *wl, cmd_t *cmd, char **argv)
{
int32 val, err;
if ((err = wl_var_get(wl, cmd, argv)))
return (err);
val = dtoh32(*(int32*)buf);
switch (val) {
case 0:
printf("SROM\n");
break;
case 1:
printf("OTP\n");
break;
case 2:
printf("NVRAM\n");
break;
default:
printf("Unrecognized source %d\n", val);
break;
}
return 0;
}
#include <bcmnvram.h>
static int
wl_otpraw(void *wl, cmd_t *cmd, char **argv)
{
char var[392];
uint32 offset;
uint32 bits;
uint32 len;
bool get = TRUE;
void *ptr = NULL;
char *endptr;
uint32 i;
if (argv[1]) {
offset = htod32(strtoul(argv[1], &endptr, 0));
memcpy(var, (char *)&offset, sizeof(offset));
len = sizeof(offset);
}
else
return BCME_USAGE_ERROR;
if (argv[2]) {
bits = htod32(strtoul(argv[2], &endptr, 0));
if (bits > 3072)
{
printf("bit size (%d) too long or negative!!\n", bits);
return BCME_BADARG;
}
}
else
bits = 1;
memcpy(&var[len], (char *)&bits, sizeof(bits));
len += sizeof(bits);
if (argv[3]) {
unsigned char data[768];
uint32 patlen;
char *inptr = argv[3];
get = FALSE;
if (*inptr == '0' && toupper((int)(*(inptr + 1))) == 'X')
inptr += 2;
patlen = strlen(inptr);
if (patlen > 768 || (patlen * 4) < bits)
{
printf("data length (%d) too long or small!!\n", patlen);
return BCME_USAGE_ERROR;
}
for (i = 1; i <= patlen; i++)
{
int n = (int)((unsigned char)*inptr++);
if (!isxdigit(n)) {
fprintf(stderr, "invalid hex digit %c\n", n);
return BCME_USAGE_ERROR;
}
data[patlen - i] = (unsigned char)(isdigit(n) ? (n - '0')
: ((islower(n) ? (toupper(n)) : n) - 'A' + 10));
}
for (i = 0; i < patlen; i += 2)
{
unsigned char v;
v = data[i];
if (i + 1 < patlen)
v += (data[i+1] * 16);
memcpy(&var[len], (char *)&v, sizeof(v));
len += sizeof(v);
}
printf("OTP RAM Write:");
for (i = 0; i < bits; i += 8)
{
unsigned char v;
v = var[2*sizeof(uint32) + (i/8)];
if ((i % 64) == 0)
printf("\nbit %4d:", offset + i);
printf(" 0x%x", v);
}
printf("\n");
}
if (get) {
int ret;
unsigned char v, *cptr;
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0) {
printf("Error reading from OTP data\n");
return ret;
}
cptr = (unsigned char *)ptr;
printf("OTP RAM Read:");
for (i = 0; i < bits; i += 8)
{
v = *cptr++;
if ((i % 64) == 0)
printf("\nbit %4d:", offset + i);
printf(" 0x%02x", v);
}
printf("\n");
return 0;
}
return wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
}
static int
wl_otpw(void *wl, cmd_t *cmd, char **argv)
{
#if !defined(BWL_FILESYSTEM_SUPPORT)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return (-1);
#elif defined(DONGLEBUILD)
UNUSED_PARAMETER(wl); UNUSED_PARAMETER(cmd); UNUSED_PARAMETER(argv);
return 0;
#else
FILE *fp;
int ret = 0;
struct nvram_header *nvr;
char *p, otpw_buf[1024 - 128];
const char *msg;
int len;
if (!*++argv)
return BCME_USAGE_ERROR;
if (!(fp = fopen(*argv, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", *argv);
return BCME_BADARG;
}
len = fread(otpw_buf, 1, sizeof(otpw_buf) - 1, fp);
if ((ret = ferror(fp))) {
printf("\nerror %d reading %s\n", ret, *argv);
ret = BCME_ERROR;
goto out;
}
if (!feof(fp)) {
printf("\nFile %s too large\n", *argv);
ret = BCME_ERROR;
goto out;
}
/* Got the bits, do they look like the output of nvserial? */
nvr = (struct nvram_header *)otpw_buf;
if (nvr->magic == NVRAM_MAGIC) {
if (cmd->set == WLC_OTPW) {
printf("File %s looks like an nvserial file, use nvotpw\n", *argv);
fflush(stdout);
ret = BCME_ERROR;
goto out;
}
len = nvr->len - sizeof(struct nvram_header);
if (len <= 0) {
printf("Invalid length (%d)\n", len);
ret = BCME_ERROR;
goto out;
}
if (len & 1) {
otpw_buf[len++] = '\0';
}
p = (char *)(nvr + 1);
msg = "nvserial";
} else {
if (cmd->set == WLC_NVOTPW) {
printf("File %s is not an nvserial file\n", *argv);
ret = BCME_ERROR;
goto out;
}
if (len & 1) {
printf("File %s has an odd length (%d)\n", *argv, len);
ret = BCME_ERROR;
goto out;
}
p = otpw_buf;
msg = "raw";
}
printf("Writing %d bytes from %s file %s to otp ...\n", len, msg, *argv);
fflush(stdout);
if ((ret = wlu_set(wl, cmd->set, p, len)) < 0) {
printf("\nError %d writing %s to otp\n", ret, *argv);
}
out:
fclose(fp);
return ret;
#endif /* BWL_FILESYSTEM_SUPPORT */
}
int wlu_srwrite_data(void *wl, cmd_t *cmd, char **argv)
{
int ret, len, nw;
uint16 *words = (uint16 *)&buf[8];
uint16 caldata_offset;
FILE *fp = NULL;
srom_rw_t *srt = (srom_rw_t *)buf;
char *arg;
char *cal_buf;
int argc;
for (argc = 0; argv[argc]; argc++);
if (argc != 4)
return BCME_USAGE_ERROR;
/* We need at least one arg */
if (!*++argv)
return BCME_USAGE_ERROR;
arg = *argv++;
if (!strcmp(arg, "-t") && !strcmp(*argv++, "calblob")) {
arg = *argv++;
/*
* Avoid wl utility to driver compatibility issues by reading a 'safe' amount of words from
* SPROM to determine the SPROM version that the driver supports, once the version is known
* the full SPROM contents can be read. At the moment sromrev12 is the largest.
*/
nw = MAX(MAX(SROM10_SIGN, SROM11_SIGN), SROM11_SIGN) + 1;
srt->byteoff = htod32(0);
srt->nbytes = htod32(2 * nw);
if (cmd->get < 0)
return BCME_ERROR;
if ((ret = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return ret;
caldata_offset = words[SROM15_CAL_OFFSET_LOC] & 0xff;
if (words[SROM11_SIGN] == SROM15_SIGNATURE) {
nw = SROM15_WORDS;
} else if (words[SROM16_SIGN] == SROM16_SIGNATURE) {
nw = SROM16_WORDS;
caldata_offset = SROM16_CAL_DATA_OFFSET;
printf("Srom16, byte offset: %d\n", caldata_offset);
} else {
printf("Unsupported for SROM revs other than rev15\n");
return BCME_ERROR;
}
/* Reading caldata from msf file */
if (!(fp = fopen(arg, "rb"))) {
fprintf(stderr, "%s: No such file or directory\n", arg);
return BCME_BADARG;
}
cal_buf = malloc(SROM_MAX);
if (cal_buf == NULL) {
ret = BCME_NOMEM;
goto out;
}
len = fread(cal_buf, 1, SROM_MAX + 1, fp);
len = (len + 1) & ~1;
if (len > SROM15_MAX_CAL_SIZE) {
ret = BCME_BUFTOOLONG;
goto out;
}
if ((ret = ferror(fp))) {
printf("\nerror %d reading %s\n", ret, arg);
ret = BCME_ERROR;
goto out;
}
if (!feof(fp)) {
printf("\nFile %s is too large\n", arg);
ret = BCME_ERROR;
goto out;
}
if ((len - MAX_IOCTL_TXCHUNK_SIZE) > 0) {
memcpy(srt->buf, cal_buf, MAX_IOCTL_TXCHUNK_SIZE);
srt->byteoff = htod32(caldata_offset);
srt->nbytes = htod32(MAX_IOCTL_TXCHUNK_SIZE);
ret = wlu_set(wl, cmd->set, buf, MAX_IOCTL_TXCHUNK_SIZE + 8);
memcpy(srt->buf, cal_buf + MAX_IOCTL_TXCHUNK_SIZE,
len - MAX_IOCTL_TXCHUNK_SIZE);
srt->byteoff = htod32(caldata_offset + MAX_IOCTL_TXCHUNK_SIZE);
srt->nbytes = htod32(len - MAX_IOCTL_TXCHUNK_SIZE);
ret = wlu_set(wl, cmd->set, buf, len - MAX_IOCTL_TXCHUNK_SIZE + 8);
}
else {
memcpy(srt->buf, cal_buf, len);
srt->byteoff = htod32(caldata_offset);
srt->nbytes = htod32(len);
ret = wlu_set(wl, cmd->set, buf, len + 8);
}
}
else {
printf("Invalid arguments for srwrite_data\n");
return BCME_BADARG;
}
out:
fflush(stdout);
if (fp)
fclose(fp);
free(cal_buf);
return ret;
}
/*
* wlu_reg3args is a generic function that is used for setting/getting
* WL_IOVAR variables that require address + offset for read, and
* address + offset + data for write.
*/
int
wlu_reg3args(void *wl, cmd_t *cmd, char **argv)
{
char var[256];
uint32 int_val;
bool get = TRUE;
uint32 len, i;
void *ptr = NULL;
char *endptr;
uint numargs;
int ret = 0;
len = 0;
if (!argv[1] || !argv[2]) {
printf("Wrong syntax => dev offset [val]\n");
return BCME_USAGE_ERROR;
}
if (argv[3]) {
numargs = 3;
get = FALSE;
} else
numargs = 2;
for (i = 1; i <= numargs; i++) {
int_val = htod32(strtoul(argv[i], &endptr, 0));
memcpy(&var[len], (char *)&int_val, sizeof(int_val));
len += sizeof(int_val);
}
if (get) {
if ((ret = wlu_var_getbuf(wl, cmd->name, var, sizeof(var), &ptr)) < 0)
return ret;
printf("0x%x\n", dtoh32(*(int *)ptr));
}
else
ret = wlu_var_setbuf(wl, cmd->name, &var, sizeof(var));
return ret;
}
static int
wl_var_getinthex(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 val;
if ((err = wl_var_get(wl, cmd, argv)))
return (err);
val = dtoh32(*(int32*)buf);
printf("0x%08x\n", val);
return 0;
}
/* Variation: Like getandprint, but allow an int arg to be passed */
static int
wl_var_setintandprintstr(void *wl, cmd_t *cmd, char **argv)
{
int err;
int32 val;
char *varname;
char *endptr = NULL;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("set: missing arguments\n");
return BCME_USAGE_ERROR;
}
varname = *argv++;
if (!*argv) {
val = 0;
} else {
val = strtol(*argv, &endptr, 0);
if (*endptr != '\0') {
/* not all the value string was parsed by strtol */
printf("set: error parsing value \"%s\" as an integer for set of \"%s\"\n",
*argv, varname);
return BCME_USAGE_ERROR;
}
}
val = htod32(val);
err = wlu_iovar_getbuf(wl, varname, &val, sizeof(int), buf, WLC_IOCTL_MAXLEN);
if (err)
return (err);
printf("%s\n", buf);
return (0);
}
#if WL_OTPREAD_VER != 1
#error "Update this code to handle the new version of wl_otpread_cmd_t !"
#endif
static int
wl_otpdump_iter(void *wl, cmd_t *cmd, char **argv)
{
int ret;
int otpsize, readsize, offset = 0;
uint i;
uint16 *outbuf = (uint16 *)buf;
wl_otpread_cmd_t read_cmd;
UNUSED_PARAMETER(cmd);
if (!*argv) {
printf("set: missing arguments\n");
return BCME_USAGE_ERROR;
}
if ((ret = wlu_iovar_getint(wl, "otpsize", &otpsize)) < 0) {
return wl_var_setintandprintstr(wl, cmd, argv);
}
readsize = otpsize = dtoh32(otpsize);
printf("otpsize: %d\n", otpsize);
read_cmd.version = WL_OTPREAD_VER;
read_cmd.cmd_len = sizeof(read_cmd);
read_cmd.rdmode = 0;
while (readsize) {
read_cmd.rdsize = (readsize <= WLC_IOCTL_MAXLEN) ? readsize : WLC_IOCTL_MAXLEN;
read_cmd.rdoffset = offset;
memset(buf, 0, WLC_IOCTL_MAXLEN);
ret = wlu_iovar_getbuf(wl, "otpread", &read_cmd, sizeof(read_cmd),
buf, WLC_IOCTL_MAXLEN);
if (ret < 0)
return ret;
for (i = 0; i < (read_cmd.rdsize / 2); i++) {
if ((i % 4) == 0) {
printf("\n0x%04x:", 2 * i + offset);
}
printf(" 0x%04x", outbuf[i]);
}
readsize -= read_cmd.rdsize;
offset += read_cmd.rdsize;
}
printf("\n\n");
return (0);
}
void
wl_printlasterror(void *wl)
{
char error_str[128];
if (wlu_iovar_get(wl, "bcmerrorstr", error_str, sizeof(error_str)) != 0) {
fprintf(stderr, "%s: \nError getting the last error\n", wlu_av0);
} else {
fprintf(stderr, "%s: %s\n", wlu_av0, error_str);
}
}
static int
wl_devpath(void *wl, cmd_t *cmd, char **argv)
{
int err;
void *ptr;
char *pbuf = buf;
UNUSED_PARAMETER(argv);
if ((err = wlu_var_getbuf_sm (wl, cmd->name, NULL, 0, &ptr)))
return (err);
pbuf += strlen(buf);
sprintf(pbuf, "\n");
fputs(buf, stdout);
return (0);
}
static int
wl_diag(void *wl, cmd_t *cmd, char **argv)
{
uint testindex;
int buflen, err;
char *param;
uint32 testresult;
if (!*++argv) {
printf(" Usage: %s testindex[1-4]\n", cmd->name);
return BCME_USAGE_ERROR;
}
testindex = atoi(*argv);
strcpy(buf, "diag");
buflen = strlen(buf) + 1;
param = (char *)(buf + buflen);
testindex = htod32(testindex);
memcpy(param, (char*)&testindex, sizeof(testindex));
if ((err = wlu_get(wl, cmd->get, buf, WLC_IOCTL_MAXLEN)) < 0)
return err;
testresult = *(uint32 *)buf;
testindex = dtoh32(testindex);
testresult = dtoh32(testresult);
if (testresult != 0) {
printf("\ndiag test %d failed(error code %d)\n", testindex, testresult);
} else
printf("\ndiag test %d passed\n", testindex);
return (0);
}