1/* 2 * Copyright (c) 2013 Broadcom Corporation 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17#include <linux/kernel.h> 18#include <linux/slab.h> 19#include <linux/device.h> 20#include <linux/firmware.h> 21#include <linux/module.h> 22 23#include "dhd_dbg.h" 24#include "firmware.h" 25 26char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; 27module_param_string(firmware_path, brcmf_firmware_path, 28 BRCMF_FW_PATH_LEN, 0440); 29 30enum nvram_parser_state { 31 IDLE, 32 KEY, 33 VALUE, 34 COMMENT, 35 END 36}; 37 38/** 39 * struct nvram_parser - internal info for parser. 40 * 41 * @state: current parser state. 42 * @fwnv: input buffer being parsed. 43 * @nvram: output buffer with parse result. 44 * @nvram_len: lenght of parse result. 45 * @line: current line. 46 * @column: current column in line. 47 * @pos: byte offset in input buffer. 48 * @entry: start position of key,value entry. 49 */ 50struct nvram_parser { 51 enum nvram_parser_state state; 52 const struct firmware *fwnv; 53 u8 *nvram; 54 u32 nvram_len; 55 u32 line; 56 u32 column; 57 u32 pos; 58 u32 entry; 59}; 60 61static bool is_nvram_char(char c) 62{ 63 /* comment marker excluded */ 64 if (c == '#') 65 return false; 66 67 /* key and value may have any other readable character */ 68 return (c > 0x20 && c < 0x7f); 69} 70 71static bool is_whitespace(char c) 72{ 73 return (c == ' ' || c == '\r' || c == '\n' || c == '\t'); 74} 75 76static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp) 77{ 78 char c; 79 80 c = nvp->fwnv->data[nvp->pos]; 81 if (c == '\n') 82 return COMMENT; 83 if (is_whitespace(c)) 84 goto proceed; 85 if (c == '#') 86 return COMMENT; 87 if (is_nvram_char(c)) { 88 nvp->entry = nvp->pos; 89 return KEY; 90 } 91 brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n", 92 nvp->line, nvp->column); 93proceed: 94 nvp->column++; 95 nvp->pos++; 96 return IDLE; 97} 98 99static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp) 100{ 101 enum nvram_parser_state st = nvp->state; 102 char c; 103 104 c = nvp->fwnv->data[nvp->pos]; 105 if (c == '=') { 106 st = VALUE; 107 } else if (!is_nvram_char(c)) { 108 brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", 109 nvp->line, nvp->column); 110 return COMMENT; 111 } 112 113 nvp->column++; 114 nvp->pos++; 115 return st; 116} 117 118static enum nvram_parser_state 119brcmf_nvram_handle_value(struct nvram_parser *nvp) 120{ 121 char c; 122 char *skv; 123 char *ekv; 124 u32 cplen; 125 126 c = nvp->fwnv->data[nvp->pos]; 127 if (!is_nvram_char(c)) { 128 /* key,value pair complete */ 129 ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; 130 skv = (u8 *)&nvp->fwnv->data[nvp->entry]; 131 cplen = ekv - skv; 132 /* copy to output buffer */ 133 memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); 134 nvp->nvram_len += cplen; 135 nvp->nvram[nvp->nvram_len] = '\0'; 136 nvp->nvram_len++; 137 return IDLE; 138 } 139 nvp->pos++; 140 nvp->column++; 141 return VALUE; 142} 143 144static enum nvram_parser_state 145brcmf_nvram_handle_comment(struct nvram_parser *nvp) 146{ 147 char *eol, *sol; 148 149 sol = (char *)&nvp->fwnv->data[nvp->pos]; 150 eol = strchr(sol, '\n'); 151 if (eol == NULL) 152 return END; 153 154 /* eat all moving to next line */ 155 nvp->line++; 156 nvp->column = 1; 157 nvp->pos += (eol - sol) + 1; 158 return IDLE; 159} 160 161static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) 162{ 163 /* final state */ 164 return END; 165} 166 167static enum nvram_parser_state 168(*nv_parser_states[])(struct nvram_parser *nvp) = { 169 brcmf_nvram_handle_idle, 170 brcmf_nvram_handle_key, 171 brcmf_nvram_handle_value, 172 brcmf_nvram_handle_comment, 173 brcmf_nvram_handle_end 174}; 175 176static int brcmf_init_nvram_parser(struct nvram_parser *nvp, 177 const struct firmware *nv) 178{ 179 memset(nvp, 0, sizeof(*nvp)); 180 nvp->fwnv = nv; 181 /* Alloc for extra 0 byte + roundup by 4 + length field */ 182 nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); 183 if (!nvp->nvram) 184 return -ENOMEM; 185 186 nvp->line = 1; 187 nvp->column = 1; 188 return 0; 189} 190 191/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil 192 * and ending in a NUL. Removes carriage returns, empty lines, comment lines, 193 * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. 194 * End of buffer is completed with token identifying length of buffer. 195 */ 196static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) 197{ 198 struct nvram_parser nvp; 199 u32 pad; 200 u32 token; 201 __le32 token_le; 202 203 if (brcmf_init_nvram_parser(&nvp, nv) < 0) 204 return NULL; 205 206 while (nvp.pos < nv->size) { 207 nvp.state = nv_parser_states[nvp.state](&nvp); 208 if (nvp.state == END) 209 break; 210 } 211 pad = nvp.nvram_len; 212 *new_length = roundup(nvp.nvram_len + 1, 4); 213 while (pad != *new_length) { 214 nvp.nvram[pad] = 0; 215 pad++; 216 } 217 218 token = *new_length / 4; 219 token = (~token << 16) | (token & 0x0000FFFF); 220 token_le = cpu_to_le32(token); 221 222 memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); 223 *new_length += sizeof(token_le); 224 225 return nvp.nvram; 226} 227 228void brcmf_fw_nvram_free(void *nvram) 229{ 230 kfree(nvram); 231} 232 233struct brcmf_fw { 234 struct device *dev; 235 u16 flags; 236 const struct firmware *code; 237 const char *nvram_name; 238 void (*done)(struct device *dev, const struct firmware *fw, 239 void *nvram_image, u32 nvram_len); 240}; 241 242static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) 243{ 244 struct brcmf_fw *fwctx = ctx; 245 u32 nvram_length = 0; 246 void *nvram = NULL; 247 248 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); 249 if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) 250 goto fail; 251 252 if (fw) { 253 nvram = brcmf_fw_nvram_strip(fw, &nvram_length); 254 release_firmware(fw); 255 if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) 256 goto fail; 257 } 258 259 fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); 260 kfree(fwctx); 261 return; 262 263fail: 264 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); 265 if (fwctx->code) 266 release_firmware(fwctx->code); 267 device_release_driver(fwctx->dev); 268 kfree(fwctx); 269} 270 271static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) 272{ 273 struct brcmf_fw *fwctx = ctx; 274 int ret; 275 276 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); 277 if (!fw) 278 goto fail; 279 280 /* only requested code so done here */ 281 if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { 282 fwctx->done(fwctx->dev, fw, NULL, 0); 283 kfree(fwctx); 284 return; 285 } 286 fwctx->code = fw; 287 ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, 288 fwctx->dev, GFP_KERNEL, fwctx, 289 brcmf_fw_request_nvram_done); 290 291 if (!ret) 292 return; 293 294 /* when nvram is optional call .done() callback here */ 295 if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { 296 fwctx->done(fwctx->dev, fw, NULL, 0); 297 kfree(fwctx); 298 return; 299 } 300 301 /* failed nvram request */ 302 release_firmware(fw); 303fail: 304 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); 305 device_release_driver(fwctx->dev); 306 kfree(fwctx); 307} 308 309int brcmf_fw_get_firmwares(struct device *dev, u16 flags, 310 const char *code, const char *nvram, 311 void (*fw_cb)(struct device *dev, 312 const struct firmware *fw, 313 void *nvram_image, u32 nvram_len)) 314{ 315 struct brcmf_fw *fwctx; 316 317 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); 318 if (!fw_cb || !code) 319 return -EINVAL; 320 321 if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) 322 return -EINVAL; 323 324 fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); 325 if (!fwctx) 326 return -ENOMEM; 327 328 fwctx->dev = dev; 329 fwctx->flags = flags; 330 fwctx->done = fw_cb; 331 if (flags & BRCMF_FW_REQUEST_NVRAM) 332 fwctx->nvram_name = nvram; 333 334 return request_firmware_nowait(THIS_MODULE, true, code, dev, 335 GFP_KERNEL, fwctx, 336 brcmf_fw_request_code_done); 337} 338