Skip to content

Commit

Permalink
brcmfmac: Create common nvram parsing routines.
Browse files Browse the repository at this point in the history
New bus layers like pcie require nvram parsing routines which are
the same routines as being used by sdio. Make these routines common
in the new file nvram.c. Update sdio to use these routines and
simplify the nvram upload process. Also add memory validation check
for downloaded firmware and nvram in debug mode.

Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
  • Loading branch information
Hante Meuleman authored and John W. Linville committed Jan 16, 2014
1 parent 2b755bb commit a74d036
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 190 deletions.
1 change: 1 addition & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ brcmfmac-objs += \
bcdc.o \
dhd_common.o \
dhd_linux.o \
nvram.o \
btcoex.o
brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \
dhd_sdio.o \
Expand Down
166 changes: 83 additions & 83 deletions drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
#include <soc.h>
#include "sdio_host.h"
#include "sdio_chip.h"
#include "nvram.h"

#define DCMD_RESP_TIMEOUT 2000 /* In milli second */

Expand Down Expand Up @@ -369,8 +370,6 @@ struct brcmf_sdio_hdrinfo {
struct brcmf_sdio {
struct brcmf_sdio_dev *sdiodev; /* sdio device handler */
struct chip_info *ci; /* Chip info struct */
char *vars; /* Variables (from CIS and/or other) */
uint varsz; /* Size of variables buffer */

u32 ramsize; /* Size of RAM in SOCRAM (bytes) */

Expand Down Expand Up @@ -3207,8 +3206,7 @@ static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter)

brcmf_sdio_chip_enter_download(bus->sdiodev, ci);
} else {
if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars,
bus->varsz))
if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci))
return false;

/* Allow HT Clock now that the ARM is running. */
Expand All @@ -3220,6 +3218,60 @@ static bool brcmf_sdio_download_state(struct brcmf_sdio *bus, bool enter)
return true;
}

#ifdef DEBUG
static bool
brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
u8 *ram_data, uint ram_sz)
{
char *ram_cmp;
int err;
bool ret = true;
int address;
int offset;
int len;

/* read back and verify */
brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr,
ram_sz);
ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL);
/* do not proceed while no memory but */
if (!ram_cmp)
return true;

address = ram_addr;
offset = 0;
while (offset < ram_sz) {
len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK :
ram_sz - offset;
err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len);
if (err) {
brcmf_err("error %d on reading %d membytes at 0x%08x\n",
err, len, address);
ret = false;
break;
} else if (memcmp(ram_cmp, &ram_data[offset], len)) {
brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n",
offset, len);
ret = false;
break;
}
offset += len;
address += len;
}

kfree(ram_cmp);

return ret;
}
#else /* DEBUG */
static bool
brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr,
u8 *ram_data, uint ram_sz)
{
return true;
}
#endif /* DEBUG */

static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus)
{
const struct firmware *fw;
Expand All @@ -3228,6 +3280,8 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus)
int address;
int len;

brcmf_dbg(TRACE, "Enter\n");

fw = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_BIN);
if (fw == NULL)
return -ENOENT;
Expand All @@ -3252,101 +3306,48 @@ static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus)
offset += len;
address += len;
}
if (!err)
if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase,
(u8 *)fw->data, fw->size))
err = -EIO;

failure:
release_firmware(fw);

return err;
}

/*
* ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
* and ending in a NUL.
* Removes carriage returns, empty lines, comment lines, and converts
* newlines to NULs.
* Shortens buffer as needed and pads with NULs. End of buffer is marked
* by two NULs.
*/

static int brcmf_sdio_strip_nvram(struct brcmf_sdio *bus,
const struct firmware *nv)
{
char *varbuf;
char *dp;
bool findNewline;
int column;
int ret = 0;
uint buf_len, n, len;

len = nv->size;
varbuf = vmalloc(len);
if (!varbuf)
return -ENOMEM;

memcpy(varbuf, nv->data, len);
dp = varbuf;

findNewline = false;
column = 0;

for (n = 0; n < len; n++) {
if (varbuf[n] == 0)
break;
if (varbuf[n] == '\r')
continue;
if (findNewline && varbuf[n] != '\n')
continue;
findNewline = false;
if (varbuf[n] == '#') {
findNewline = true;
continue;
}
if (varbuf[n] == '\n') {
if (column == 0)
continue;
*dp++ = 0;
column = 0;
continue;
}
*dp++ = varbuf[n];
column++;
}
buf_len = dp - varbuf;
while (dp < varbuf + n)
*dp++ = 0;

kfree(bus->vars);
/* roundup needed for download to device */
bus->varsz = roundup(buf_len + 1, 4);
bus->vars = kmalloc(bus->varsz, GFP_KERNEL);
if (bus->vars == NULL) {
bus->varsz = 0;
ret = -ENOMEM;
goto err;
}

/* copy the processed variables and add null termination */
memcpy(bus->vars, varbuf, buf_len);
bus->vars[buf_len] = 0;
err:
vfree(varbuf);
return ret;
}

static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus)
{
const struct firmware *nv;
int ret;
void *vars;
u32 varsz;
int address;
int err;

brcmf_dbg(TRACE, "Enter\n");

nv = brcmf_sdio_get_fw(bus, BRCMF_FIRMWARE_NVRAM);
if (nv == NULL)
return -ENOENT;

ret = brcmf_sdio_strip_nvram(bus, nv);

vars = brcmf_nvram_strip(nv, &varsz);
release_firmware(nv);

return ret;
if (vars == NULL)
return -EINVAL;

address = bus->ci->ramsize - varsz + bus->ci->rambase;
err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz);
if (err)
brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n",
err, varsz, address);
else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz))
err = -EIO;

brcmf_nvram_free(vars);

return err;
}

static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus)
Expand Down Expand Up @@ -4092,7 +4093,6 @@ void brcmf_sdio_remove(struct brcmf_sdio *bus)
brcmu_pkt_buf_free_skb(bus->txglom_sgpad);
kfree(bus->rxbuf);
kfree(bus->hdrbuf);
kfree(bus->vars);
kfree(bus);
}

Expand Down
94 changes: 94 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/nvram.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (c) 2013 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/firmware.h>

#include "nvram.h"

/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a file
* and ending in a NUL. Removes carriage returns, empty lines, comment lines,
* and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
* End of buffer is completed with token identifying length of buffer.
*/
void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length)
{
u8 *nvram;
u32 i;
u32 len;
u32 column;
u8 val;
bool comment;
u32 token;
__le32 token_le;

/* Alloc for extra 0 byte + roundup by 4 + length field */
nvram = kmalloc(nv->size + 1 + 3 + sizeof(token_le), GFP_KERNEL);
if (!nvram)
return NULL;

len = 0;
column = 0;
comment = false;
for (i = 0; i < nv->size; i++) {
val = nv->data[i];
if (val == 0)
break;
if (val == '\r')
continue;
if (comment && (val != '\n'))
continue;
comment = false;
if (val == '#') {
comment = true;
continue;
}
if (val == '\n') {
if (column == 0)
continue;
nvram[len] = 0;
len++;
column = 0;
continue;
}
nvram[len] = val;
len++;
column++;
}
column = len;
*new_length = roundup(len + 1, 4);
while (column != *new_length) {
nvram[column] = 0;
column++;
}

token = *new_length / 4;
token = (~token << 16) | (token & 0x0000FFFF);
token_le = cpu_to_le32(token);

memcpy(&nvram[*new_length], &token_le, sizeof(token_le));
*new_length += sizeof(token_le);

return nvram;
}

void brcmf_nvram_free(void *nvram)
{
kfree(nvram);
}


24 changes: 24 additions & 0 deletions drivers/net/wireless/brcm80211/brcmfmac/nvram.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2013 Broadcom Corporation
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef BRCMFMAC_NVRAM_H
#define BRCMFMAC_NVRAM_H


void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length);
void brcmf_nvram_free(void *nvram);


#endif /* BRCMFMAC_NVRAM_H */
Loading

0 comments on commit a74d036

Please sign in to comment.