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 "debug.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 /* ignore RAW1 by treating as comment */ 107 if (strncmp(&nvp->fwnv->data[nvp->entry], "RAW1", 4) == 0) 108 st = COMMENT; 109 else 110 st = VALUE; 111 } else if (!is_nvram_char(c)) { 112 brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n", 113 nvp->line, nvp->column); 114 return COMMENT; 115 } 116 117 nvp->column++; 118 nvp->pos++; 119 return st; 120} 121 122static enum nvram_parser_state 123brcmf_nvram_handle_value(struct nvram_parser *nvp) 124{ 125 char c; 126 char *skv; 127 char *ekv; 128 u32 cplen; 129 130 c = nvp->fwnv->data[nvp->pos]; 131 if (!is_nvram_char(c)) { 132 /* key,value pair complete */ 133 ekv = (u8 *)&nvp->fwnv->data[nvp->pos]; 134 skv = (u8 *)&nvp->fwnv->data[nvp->entry]; 135 cplen = ekv - skv; 136 /* copy to output buffer */ 137 memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen); 138 nvp->nvram_len += cplen; 139 nvp->nvram[nvp->nvram_len] = '\0'; 140 nvp->nvram_len++; 141 return IDLE; 142 } 143 nvp->pos++; 144 nvp->column++; 145 return VALUE; 146} 147 148static enum nvram_parser_state 149brcmf_nvram_handle_comment(struct nvram_parser *nvp) 150{ 151 char *eol, *sol; 152 153 sol = (char *)&nvp->fwnv->data[nvp->pos]; 154 eol = strchr(sol, '\n'); 155 if (eol == NULL) 156 return END; 157 158 /* eat all moving to next line */ 159 nvp->line++; 160 nvp->column = 1; 161 nvp->pos += (eol - sol) + 1; 162 return IDLE; 163} 164 165static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp) 166{ 167 /* final state */ 168 return END; 169} 170 171static enum nvram_parser_state 172(*nv_parser_states[])(struct nvram_parser *nvp) = { 173 brcmf_nvram_handle_idle, 174 brcmf_nvram_handle_key, 175 brcmf_nvram_handle_value, 176 brcmf_nvram_handle_comment, 177 brcmf_nvram_handle_end 178}; 179 180static int brcmf_init_nvram_parser(struct nvram_parser *nvp, 181 const struct firmware *nv) 182{ 183 memset(nvp, 0, sizeof(*nvp)); 184 nvp->fwnv = nv; 185 /* Alloc for extra 0 byte + roundup by 4 + length field */ 186 nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL); 187 if (!nvp->nvram) 188 return -ENOMEM; 189 190 nvp->line = 1; 191 nvp->column = 1; 192 return 0; 193} 194 195/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil 196 * and ending in a NUL. Removes carriage returns, empty lines, comment lines, 197 * and converts newlines to NULs. Shortens buffer as needed and pads with NULs. 198 * End of buffer is completed with token identifying length of buffer. 199 */ 200static void *brcmf_fw_nvram_strip(const struct firmware *nv, u32 *new_length) 201{ 202 struct nvram_parser nvp; 203 u32 pad; 204 u32 token; 205 __le32 token_le; 206 207 if (brcmf_init_nvram_parser(&nvp, nv) < 0) 208 return NULL; 209 210 while (nvp.pos < nv->size) { 211 nvp.state = nv_parser_states[nvp.state](&nvp); 212 if (nvp.state == END) 213 break; 214 } 215 pad = nvp.nvram_len; 216 *new_length = roundup(nvp.nvram_len + 1, 4); 217 while (pad != *new_length) { 218 nvp.nvram[pad] = 0; 219 pad++; 220 } 221 222 token = *new_length / 4; 223 token = (~token << 16) | (token & 0x0000FFFF); 224 token_le = cpu_to_le32(token); 225 226 memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le)); 227 *new_length += sizeof(token_le); 228 229 return nvp.nvram; 230} 231 232void brcmf_fw_nvram_free(void *nvram) 233{ 234 kfree(nvram); 235} 236 237struct brcmf_fw { 238 struct device *dev; 239 u16 flags; 240 const struct firmware *code; 241 const char *nvram_name; 242 void (*done)(struct device *dev, const struct firmware *fw, 243 void *nvram_image, u32 nvram_len); 244}; 245 246static void brcmf_fw_request_nvram_done(const struct firmware *fw, void *ctx) 247{ 248 struct brcmf_fw *fwctx = ctx; 249 u32 nvram_length = 0; 250 void *nvram = NULL; 251 252 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); 253 if (!fw && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) 254 goto fail; 255 256 if (fw) { 257 nvram = brcmf_fw_nvram_strip(fw, &nvram_length); 258 release_firmware(fw); 259 if (!nvram && !(fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL)) 260 goto fail; 261 } 262 263 fwctx->done(fwctx->dev, fwctx->code, nvram, nvram_length); 264 kfree(fwctx); 265 return; 266 267fail: 268 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); 269 release_firmware(fwctx->code); 270 device_release_driver(fwctx->dev); 271 kfree(fwctx); 272} 273 274static void brcmf_fw_request_code_done(const struct firmware *fw, void *ctx) 275{ 276 struct brcmf_fw *fwctx = ctx; 277 int ret; 278 279 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(fwctx->dev)); 280 if (!fw) 281 goto fail; 282 283 /* only requested code so done here */ 284 if (!(fwctx->flags & BRCMF_FW_REQUEST_NVRAM)) { 285 fwctx->done(fwctx->dev, fw, NULL, 0); 286 kfree(fwctx); 287 return; 288 } 289 fwctx->code = fw; 290 ret = request_firmware_nowait(THIS_MODULE, true, fwctx->nvram_name, 291 fwctx->dev, GFP_KERNEL, fwctx, 292 brcmf_fw_request_nvram_done); 293 294 if (!ret) 295 return; 296 297 /* when nvram is optional call .done() callback here */ 298 if (fwctx->flags & BRCMF_FW_REQ_NV_OPTIONAL) { 299 fwctx->done(fwctx->dev, fw, NULL, 0); 300 kfree(fwctx); 301 return; 302 } 303 304 /* failed nvram request */ 305 release_firmware(fw); 306fail: 307 brcmf_dbg(TRACE, "failed: dev=%s\n", dev_name(fwctx->dev)); 308 device_release_driver(fwctx->dev); 309 kfree(fwctx); 310} 311 312int brcmf_fw_get_firmwares(struct device *dev, u16 flags, 313 const char *code, const char *nvram, 314 void (*fw_cb)(struct device *dev, 315 const struct firmware *fw, 316 void *nvram_image, u32 nvram_len)) 317{ 318 struct brcmf_fw *fwctx; 319 320 brcmf_dbg(TRACE, "enter: dev=%s\n", dev_name(dev)); 321 if (!fw_cb || !code) 322 return -EINVAL; 323 324 if ((flags & BRCMF_FW_REQUEST_NVRAM) && !nvram) 325 return -EINVAL; 326 327 fwctx = kzalloc(sizeof(*fwctx), GFP_KERNEL); 328 if (!fwctx) 329 return -ENOMEM; 330 331 fwctx->dev = dev; 332 fwctx->flags = flags; 333 fwctx->done = fw_cb; 334 if (flags & BRCMF_FW_REQUEST_NVRAM) 335 fwctx->nvram_name = nvram; 336 337 return request_firmware_nowait(THIS_MODULE, true, code, dev, 338 GFP_KERNEL, fwctx, 339 brcmf_fw_request_code_done); 340} 341