blob: 5c450d11dbc917fb3bf0ed806d0067647d15a8e6 [file] [log] [blame]
Hante Meulemana74d0362014-01-13 22:20:22 +01001/*
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/firmware.h>
20
Arend van Spriel3e99b082014-05-12 10:47:31 +020021#include "dhd_dbg.h"
Hante Meulemana74d0362014-01-13 22:20:22 +010022#include "nvram.h"
23
Arend van Spriel3e99b082014-05-12 10:47:31 +020024enum nvram_parser_state {
25 IDLE,
26 KEY,
27 VALUE,
28 COMMENT,
29 END
30};
31
32/**
33 * struct nvram_parser - internal info for parser.
34 *
35 * @state: current parser state.
36 * @fwnv: input buffer being parsed.
37 * @nvram: output buffer with parse result.
38 * @nvram_len: lenght of parse result.
39 * @line: current line.
40 * @column: current column in line.
41 * @pos: byte offset in input buffer.
42 * @entry: start position of key,value entry.
43 */
44struct nvram_parser {
45 enum nvram_parser_state state;
46 const struct firmware *fwnv;
47 u8 *nvram;
48 u32 nvram_len;
49 u32 line;
50 u32 column;
51 u32 pos;
52 u32 entry;
53};
54
55static bool is_nvram_char(char c)
56{
57 /* comment marker excluded */
58 if (c == '#')
59 return false;
60
61 /* key and value may have any other readable character */
62 return (c > 0x20 && c < 0x7f);
63}
64
65static bool is_whitespace(char c)
66{
67 return (c == ' ' || c == '\r' || c == '\n' || c == '\t');
68}
69
70static enum nvram_parser_state brcmf_nvram_handle_idle(struct nvram_parser *nvp)
71{
72 char c;
73
74 c = nvp->fwnv->data[nvp->pos];
75 if (c == '\n')
76 return COMMENT;
77 if (is_whitespace(c))
78 goto proceed;
79 if (c == '#')
80 return COMMENT;
81 if (is_nvram_char(c)) {
82 nvp->entry = nvp->pos;
83 return KEY;
84 }
85 brcmf_dbg(INFO, "warning: ln=%d:col=%d: ignoring invalid character\n",
86 nvp->line, nvp->column);
87proceed:
88 nvp->column++;
89 nvp->pos++;
90 return IDLE;
91}
92
93static enum nvram_parser_state brcmf_nvram_handle_key(struct nvram_parser *nvp)
94{
95 enum nvram_parser_state st = nvp->state;
96 char c;
97
98 c = nvp->fwnv->data[nvp->pos];
99 if (c == '=') {
100 st = VALUE;
101 } else if (!is_nvram_char(c)) {
102 brcmf_dbg(INFO, "warning: ln=%d:col=%d: '=' expected, skip invalid key entry\n",
103 nvp->line, nvp->column);
104 return COMMENT;
105 }
106
107 nvp->column++;
108 nvp->pos++;
109 return st;
110}
111
112static enum nvram_parser_state
113brcmf_nvram_handle_value(struct nvram_parser *nvp)
114{
115 char c;
116 char *skv;
117 char *ekv;
118 u32 cplen;
119
120 c = nvp->fwnv->data[nvp->pos];
121 if (!is_nvram_char(c)) {
122 /* key,value pair complete */
123 ekv = (u8 *)&nvp->fwnv->data[nvp->pos];
124 skv = (u8 *)&nvp->fwnv->data[nvp->entry];
125 cplen = ekv - skv;
126 /* copy to output buffer */
127 memcpy(&nvp->nvram[nvp->nvram_len], skv, cplen);
128 nvp->nvram_len += cplen;
129 nvp->nvram[nvp->nvram_len] = '\0';
130 nvp->nvram_len++;
131 return IDLE;
132 }
133 nvp->pos++;
134 nvp->column++;
135 return VALUE;
136}
137
138static enum nvram_parser_state
139brcmf_nvram_handle_comment(struct nvram_parser *nvp)
140{
141 char *eol, *sol;
142
143 sol = (char *)&nvp->fwnv->data[nvp->pos];
144 eol = strchr(sol, '\n');
145 if (eol == NULL)
146 return END;
147
148 /* eat all moving to next line */
149 nvp->line++;
150 nvp->column = 1;
151 nvp->pos += (eol - sol) + 1;
152 return IDLE;
153}
154
155static enum nvram_parser_state brcmf_nvram_handle_end(struct nvram_parser *nvp)
156{
157 /* final state */
158 return END;
159}
160
161static enum nvram_parser_state
162(*nv_parser_states[])(struct nvram_parser *nvp) = {
163 brcmf_nvram_handle_idle,
164 brcmf_nvram_handle_key,
165 brcmf_nvram_handle_value,
166 brcmf_nvram_handle_comment,
167 brcmf_nvram_handle_end
168};
169
170static int brcmf_init_nvram_parser(struct nvram_parser *nvp,
171 const struct firmware *nv)
172{
173 memset(nvp, 0, sizeof(*nvp));
174 nvp->fwnv = nv;
175 /* Alloc for extra 0 byte + roundup by 4 + length field */
176 nvp->nvram = kzalloc(nv->size + 1 + 3 + sizeof(u32), GFP_KERNEL);
177 if (!nvp->nvram)
178 return -ENOMEM;
179
180 nvp->line = 1;
181 nvp->column = 1;
182 return 0;
183}
184
185/* brcmf_nvram_strip :Takes a buffer of "<var>=<value>\n" lines read from a fil
Hante Meulemana74d0362014-01-13 22:20:22 +0100186 * and ending in a NUL. Removes carriage returns, empty lines, comment lines,
187 * and converts newlines to NULs. Shortens buffer as needed and pads with NULs.
188 * End of buffer is completed with token identifying length of buffer.
189 */
190void *brcmf_nvram_strip(const struct firmware *nv, u32 *new_length)
191{
Arend van Spriel3e99b082014-05-12 10:47:31 +0200192 struct nvram_parser nvp;
193 u32 pad;
Hante Meulemana74d0362014-01-13 22:20:22 +0100194 u32 token;
195 __le32 token_le;
196
Arend van Spriel3e99b082014-05-12 10:47:31 +0200197 if (brcmf_init_nvram_parser(&nvp, nv) < 0)
Hante Meulemana74d0362014-01-13 22:20:22 +0100198 return NULL;
199
Arend van Spriel3e99b082014-05-12 10:47:31 +0200200 while (nvp.pos < nv->size) {
201 nvp.state = nv_parser_states[nvp.state](&nvp);
202 if (nvp.state == END)
Hante Meulemana74d0362014-01-13 22:20:22 +0100203 break;
Hante Meulemana74d0362014-01-13 22:20:22 +0100204 }
Arend van Spriel3e99b082014-05-12 10:47:31 +0200205 pad = nvp.nvram_len;
206 *new_length = roundup(nvp.nvram_len + 1, 4);
207 while (pad != *new_length) {
208 nvp.nvram[pad] = 0;
209 pad++;
Hante Meulemana74d0362014-01-13 22:20:22 +0100210 }
211
212 token = *new_length / 4;
213 token = (~token << 16) | (token & 0x0000FFFF);
214 token_le = cpu_to_le32(token);
215
Arend van Spriel3e99b082014-05-12 10:47:31 +0200216 memcpy(&nvp.nvram[*new_length], &token_le, sizeof(token_le));
Hante Meulemana74d0362014-01-13 22:20:22 +0100217 *new_length += sizeof(token_le);
218
Arend van Spriel3e99b082014-05-12 10:47:31 +0200219 return nvp.nvram;
Hante Meulemana74d0362014-01-13 22:20:22 +0100220}
221
222void brcmf_nvram_free(void *nvram)
223{
224 kfree(nvram);
225}
226