Commit | Line | Data |
---|---|---|
81f5dcb8 HM |
1 | /* |
2 | * Copyright (c) 2012 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 | /* FWIL is the Firmware Interface Layer. In this module the support functions | |
18 | * are located to set and get variables to and from the firmware. | |
19 | */ | |
20 | ||
21 | #include <linux/kernel.h> | |
22 | #include <linux/netdevice.h> | |
23 | #include <defs.h> | |
24 | #include <brcmu_utils.h> | |
25 | #include <brcmu_wifi.h> | |
26 | #include "dhd.h" | |
27 | #include "dhd_bus.h" | |
28 | #include "dhd_dbg.h" | |
ec5a07d5 | 29 | #include "fwil.h" |
81f5dcb8 HM |
30 | |
31 | ||
32 | static s32 | |
33 | brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) | |
34 | { | |
35 | struct brcmf_pub *drvr = ifp->drvr; | |
36 | s32 err; | |
37 | ||
38 | if (drvr->bus_if->state == BRCMF_BUS_DOWN) { | |
39 | brcmf_dbg(ERROR, "bus is down. we have nothing to do.\n"); | |
40 | return -EIO; | |
41 | } | |
42 | ||
43 | if (data != NULL) | |
44 | len = min_t(uint, len, BRCMF_DCMD_MAXLEN); | |
45 | if (set) | |
46 | err = brcmf_proto_cdc_set_dcmd(drvr, ifp->idx, cmd, data, len); | |
47 | else | |
48 | err = brcmf_proto_cdc_query_dcmd(drvr, ifp->idx, cmd, data, | |
49 | len); | |
50 | ||
51 | if (err >= 0) | |
52 | err = 0; | |
53 | else | |
54 | brcmf_dbg(ERROR, "Failed err=%d\n", err); | |
55 | ||
56 | return err; | |
57 | } | |
58 | ||
59 | s32 | |
60 | brcmf_fil_cmd_data_set(struct net_device *ndev, u32 cmd, void *data, u32 len) | |
61 | { | |
62 | struct brcmf_if *ifp = netdev_priv(ndev); | |
63 | s32 err; | |
64 | ||
65 | mutex_lock(&ifp->drvr->proto_block); | |
66 | ||
67 | brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); | |
68 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); | |
69 | ||
70 | err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); | |
71 | mutex_unlock(&ifp->drvr->proto_block); | |
72 | ||
73 | return err; | |
74 | } | |
75 | ||
76 | s32 | |
77 | brcmf_fil_cmd_data_get(struct net_device *ndev, u32 cmd, void *data, u32 len) | |
78 | { | |
79 | struct brcmf_if *ifp = netdev_priv(ndev); | |
80 | s32 err; | |
81 | ||
82 | mutex_lock(&ifp->drvr->proto_block); | |
83 | err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); | |
84 | ||
85 | brcmf_dbg(FIL, "cmd=%d, len=%d\n", cmd, len); | |
86 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); | |
87 | ||
88 | mutex_unlock(&ifp->drvr->proto_block); | |
89 | ||
90 | return err; | |
91 | } | |
92 | ||
93 | ||
94 | s32 | |
95 | brcmf_fil_cmd_int_set(struct net_device *ndev, u32 cmd, u32 data) | |
96 | { | |
97 | struct brcmf_if *ifp = netdev_priv(ndev); | |
98 | s32 err; | |
99 | __le32 data_le = cpu_to_le32(data); | |
100 | ||
101 | mutex_lock(&ifp->drvr->proto_block); | |
102 | err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); | |
103 | mutex_unlock(&ifp->drvr->proto_block); | |
104 | ||
105 | return err; | |
106 | } | |
107 | ||
108 | s32 | |
109 | brcmf_fil_cmd_int_get(struct net_device *ndev, u32 cmd, u32 *data) | |
110 | { | |
111 | struct brcmf_if *ifp = netdev_priv(ndev); | |
112 | s32 err; | |
113 | __le32 data_le = cpu_to_le32(*data); | |
114 | ||
115 | mutex_lock(&ifp->drvr->proto_block); | |
116 | err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); | |
117 | mutex_unlock(&ifp->drvr->proto_block); | |
118 | *data = le32_to_cpu(data_le); | |
119 | ||
120 | return err; | |
121 | } | |
122 | ||
123 | static u32 | |
124 | brcmf_create_iovar(char *name, char *data, u32 datalen, char *buf, u32 buflen) | |
125 | { | |
126 | u32 len; | |
127 | ||
128 | len = strlen(name) + 1; | |
129 | ||
130 | if ((len + datalen) > buflen) | |
131 | return 0; | |
132 | ||
133 | memcpy(buf, name, len); | |
134 | ||
135 | /* append data onto the end of the name string */ | |
136 | if (data && datalen) | |
137 | memcpy(&buf[len], data, datalen); | |
138 | ||
139 | return len + datalen; | |
140 | } | |
141 | ||
142 | ||
143 | s32 | |
144 | brcmf_fil_iovar_data_set(struct net_device *ndev, char *name, void *data, | |
145 | u32 len) | |
146 | { | |
147 | struct brcmf_if *ifp = netdev_priv(ndev); | |
148 | struct brcmf_pub *drvr = ifp->drvr; | |
149 | s32 err; | |
150 | u32 buflen; | |
151 | ||
152 | mutex_lock(&drvr->proto_block); | |
153 | ||
154 | brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); | |
155 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); | |
156 | ||
157 | buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, | |
158 | sizeof(drvr->proto_buf)); | |
159 | if (buflen) { | |
160 | err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, | |
161 | buflen, true); | |
162 | } else { | |
163 | err = -EPERM; | |
164 | brcmf_dbg(ERROR, "Creating iovar failed\n"); | |
165 | } | |
166 | ||
167 | mutex_unlock(&drvr->proto_block); | |
168 | return err; | |
169 | } | |
170 | ||
171 | s32 | |
172 | brcmf_fil_iovar_data_get(struct net_device *ndev, char *name, void *data, | |
173 | u32 len) | |
174 | { | |
175 | struct brcmf_if *ifp = netdev_priv(ndev); | |
176 | struct brcmf_pub *drvr = ifp->drvr; | |
177 | s32 err; | |
178 | u32 buflen; | |
179 | ||
180 | mutex_lock(&drvr->proto_block); | |
181 | ||
182 | buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, | |
183 | sizeof(drvr->proto_buf)); | |
184 | if (buflen) { | |
185 | err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, | |
186 | buflen, false); | |
187 | if (err == 0) | |
188 | memcpy(data, drvr->proto_buf, len); | |
189 | } else { | |
190 | err = -EPERM; | |
191 | brcmf_dbg(ERROR, "Creating iovar failed\n"); | |
192 | } | |
193 | ||
194 | brcmf_dbg(FIL, "name=%s, len=%d\n", name, len); | |
195 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); | |
196 | ||
197 | mutex_unlock(&drvr->proto_block); | |
198 | return err; | |
199 | } | |
200 | ||
201 | s32 | |
202 | brcmf_fil_iovar_int_set(struct net_device *ndev, char *name, u32 data) | |
203 | { | |
204 | __le32 data_le = cpu_to_le32(data); | |
205 | ||
206 | return brcmf_fil_iovar_data_set(ndev, name, &data_le, sizeof(data_le)); | |
207 | } | |
208 | ||
209 | s32 | |
210 | brcmf_fil_iovar_int_get(struct net_device *ndev, char *name, u32 *data) | |
211 | { | |
212 | __le32 data_le = cpu_to_le32(*data); | |
213 | s32 err; | |
214 | ||
215 | err = brcmf_fil_iovar_data_get(ndev, name, &data_le, sizeof(data_le)); | |
216 | if (err == 0) | |
217 | *data = le32_to_cpu(data_le); | |
218 | return err; | |
219 | } | |
220 | ||
221 | static u32 | |
222 | brcmf_create_bsscfg(s32 bssidx, char *name, char *data, u32 datalen, char *buf, | |
223 | u32 buflen) | |
224 | { | |
225 | const s8 *prefix = "bsscfg:"; | |
226 | s8 *p; | |
227 | u32 prefixlen; | |
228 | u32 namelen; | |
229 | u32 iolen; | |
230 | __le32 bssidx_le; | |
231 | ||
232 | if (bssidx == 0) | |
233 | return brcmf_create_iovar(name, data, datalen, buf, buflen); | |
234 | ||
235 | prefixlen = strlen(prefix); | |
236 | namelen = strlen(name) + 1; /* lengh of iovar name + null */ | |
237 | iolen = prefixlen + namelen + sizeof(bssidx_le) + datalen; | |
238 | ||
239 | if (buflen < iolen) { | |
240 | brcmf_dbg(ERROR, "buffer is too short\n"); | |
241 | return 0; | |
242 | } | |
243 | ||
244 | p = buf; | |
245 | ||
246 | /* copy prefix, no null */ | |
247 | memcpy(p, prefix, prefixlen); | |
248 | p += prefixlen; | |
249 | ||
250 | /* copy iovar name including null */ | |
251 | memcpy(p, name, namelen); | |
252 | p += namelen; | |
253 | ||
254 | /* bss config index as first data */ | |
255 | bssidx_le = cpu_to_le32(bssidx); | |
256 | memcpy(p, &bssidx_le, sizeof(bssidx_le)); | |
257 | p += sizeof(bssidx_le); | |
258 | ||
259 | /* parameter buffer follows */ | |
260 | if (datalen) | |
261 | memcpy(p, data, datalen); | |
262 | ||
263 | return iolen; | |
264 | } | |
265 | ||
266 | s32 | |
ec5a07d5 | 267 | brcmf_fil_bsscfg_data_set(struct net_device *ndev, char *name, |
81f5dcb8 HM |
268 | void *data, u32 len) |
269 | { | |
270 | struct brcmf_if *ifp = netdev_priv(ndev); | |
271 | struct brcmf_pub *drvr = ifp->drvr; | |
272 | s32 err; | |
273 | u32 buflen; | |
274 | ||
275 | mutex_lock(&drvr->proto_block); | |
276 | ||
ec5a07d5 | 277 | brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); |
81f5dcb8 HM |
278 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); |
279 | ||
ec5a07d5 AS |
280 | buflen = brcmf_create_bsscfg(ifp->bssidx, name, data, len, |
281 | drvr->proto_buf, sizeof(drvr->proto_buf)); | |
81f5dcb8 HM |
282 | if (buflen) { |
283 | err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, | |
284 | buflen, true); | |
285 | } else { | |
286 | err = -EPERM; | |
287 | brcmf_dbg(ERROR, "Creating bsscfg failed\n"); | |
288 | } | |
289 | ||
290 | mutex_unlock(&drvr->proto_block); | |
291 | return err; | |
292 | } | |
293 | ||
294 | s32 | |
ec5a07d5 | 295 | brcmf_fil_bsscfg_data_get(struct net_device *ndev, char *name, |
81f5dcb8 HM |
296 | void *data, u32 len) |
297 | { | |
298 | struct brcmf_if *ifp = netdev_priv(ndev); | |
299 | struct brcmf_pub *drvr = ifp->drvr; | |
300 | s32 err; | |
301 | u32 buflen; | |
302 | ||
303 | mutex_lock(&drvr->proto_block); | |
304 | ||
ec5a07d5 AS |
305 | buflen = brcmf_create_bsscfg(ifp->bssidx, name, NULL, len, |
306 | drvr->proto_buf, sizeof(drvr->proto_buf)); | |
81f5dcb8 HM |
307 | if (buflen) { |
308 | err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, | |
309 | buflen, false); | |
310 | if (err == 0) | |
311 | memcpy(data, drvr->proto_buf, len); | |
312 | } else { | |
313 | err = -EPERM; | |
314 | brcmf_dbg(ERROR, "Creating bsscfg failed\n"); | |
315 | } | |
ec5a07d5 | 316 | brcmf_dbg(FIL, "bssidx=%d, name=%s, len=%d\n", ifp->bssidx, name, len); |
81f5dcb8 HM |
317 | brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, len, "data"); |
318 | ||
319 | mutex_unlock(&drvr->proto_block); | |
320 | return err; | |
321 | ||
322 | } | |
323 | ||
324 | s32 | |
ec5a07d5 | 325 | brcmf_fil_bsscfg_int_set(struct net_device *ndev, char *name, u32 data) |
81f5dcb8 HM |
326 | { |
327 | __le32 data_le = cpu_to_le32(data); | |
328 | ||
ec5a07d5 | 329 | return brcmf_fil_bsscfg_data_set(ndev, name, &data_le, |
81f5dcb8 HM |
330 | sizeof(data_le)); |
331 | } | |
332 | ||
333 | s32 | |
ec5a07d5 | 334 | brcmf_fil_bsscfg_int_get(struct net_device *ndev, char *name, u32 *data) |
81f5dcb8 HM |
335 | { |
336 | __le32 data_le = cpu_to_le32(*data); | |
337 | s32 err; | |
338 | ||
ec5a07d5 | 339 | err = brcmf_fil_bsscfg_data_get(ndev, name, &data_le, |
81f5dcb8 HM |
340 | sizeof(data_le)); |
341 | if (err == 0) | |
342 | *data = le32_to_cpu(data_le); | |
343 | return err; | |
344 | } |