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 <brcmu_utils.h> 24 #include <brcmu_wifi.h> 25 #include "core.h" 26 #include "bus.h" 27 #include "debug.h" 28 #include "tracepoint.h" 29 #include "fwil.h" 30 #include "proto.h" 31 32 33 #define MAX_HEX_DUMP_LEN 64 34 35 #ifdef DEBUG 36 static const char * const brcmf_fil_errstr[] = { 37 "BCME_OK", 38 "BCME_ERROR", 39 "BCME_BADARG", 40 "BCME_BADOPTION", 41 "BCME_NOTUP", 42 "BCME_NOTDOWN", 43 "BCME_NOTAP", 44 "BCME_NOTSTA", 45 "BCME_BADKEYIDX", 46 "BCME_RADIOOFF", 47 "BCME_NOTBANDLOCKED", 48 "BCME_NOCLK", 49 "BCME_BADRATESET", 50 "BCME_BADBAND", 51 "BCME_BUFTOOSHORT", 52 "BCME_BUFTOOLONG", 53 "BCME_BUSY", 54 "BCME_NOTASSOCIATED", 55 "BCME_BADSSIDLEN", 56 "BCME_OUTOFRANGECHAN", 57 "BCME_BADCHAN", 58 "BCME_BADADDR", 59 "BCME_NORESOURCE", 60 "BCME_UNSUPPORTED", 61 "BCME_BADLEN", 62 "BCME_NOTREADY", 63 "BCME_EPERM", 64 "BCME_NOMEM", 65 "BCME_ASSOCIATED", 66 "BCME_RANGE", 67 "BCME_NOTFOUND", 68 "BCME_WME_NOT_ENABLED", 69 "BCME_TSPEC_NOTFOUND", 70 "BCME_ACM_NOTSUPPORTED", 71 "BCME_NOT_WME_ASSOCIATION", 72 "BCME_SDIO_ERROR", 73 "BCME_DONGLE_DOWN", 74 "BCME_VERSION", 75 "BCME_TXFAIL", 76 "BCME_RXFAIL", 77 "BCME_NODEVICE", 78 "BCME_NMODE_DISABLED", 79 "BCME_NONRESIDENT", 80 "BCME_SCANREJECT", 81 "BCME_USAGE_ERROR", 82 "BCME_IOCTL_ERROR", 83 "BCME_SERIAL_PORT_ERR", 84 "BCME_DISABLED", 85 "BCME_DECERR", 86 "BCME_ENCERR", 87 "BCME_MICERR", 88 "BCME_REPLAY", 89 "BCME_IE_NOTFOUND", 90 }; 91 92 static const char *brcmf_fil_get_errstr(u32 err) 93 { 94 if (err >= ARRAY_SIZE(brcmf_fil_errstr)) 95 return "(unknown)"; 96 97 return brcmf_fil_errstr[err]; 98 } 99 #else 100 static const char *brcmf_fil_get_errstr(u32 err) 101 { 102 return ""; 103 } 104 #endif /* DEBUG */ 105 106 static s32 107 brcmf_fil_cmd_data(struct brcmf_if *ifp, u32 cmd, void *data, u32 len, bool set) 108 { 109 struct brcmf_pub *drvr = ifp->drvr; 110 s32 err, fwerr; 111 112 if (drvr->bus_if->state != BRCMF_BUS_UP) { 113 brcmf_err("bus is down. we have nothing to do.\n"); 114 return -EIO; 115 } 116 117 if (data != NULL) 118 len = min_t(uint, len, BRCMF_DCMD_MAXLEN); 119 if (set) 120 err = brcmf_proto_set_dcmd(drvr, ifp->ifidx, cmd, 121 data, len, &fwerr); 122 else 123 err = brcmf_proto_query_dcmd(drvr, ifp->ifidx, cmd, 124 data, len, &fwerr); 125 126 if (err) { 127 brcmf_dbg(FIL, "Failed: error=%d\n", err); 128 } else if (fwerr < 0) { 129 brcmf_dbg(FIL, "Firmware error: %s (%d)\n", 130 brcmf_fil_get_errstr((u32)(-fwerr)), fwerr); 131 err = -EBADE; 132 } 133 if (ifp->fwil_fwerr) 134 return fwerr; 135 136 return err; 137 } 138 139 s32 140 brcmf_fil_cmd_data_set(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) 141 { 142 s32 err; 143 144 mutex_lock(&ifp->drvr->proto_block); 145 146 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); 147 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 148 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 149 150 err = brcmf_fil_cmd_data(ifp, cmd, data, len, true); 151 mutex_unlock(&ifp->drvr->proto_block); 152 153 return err; 154 } 155 156 s32 157 brcmf_fil_cmd_data_get(struct brcmf_if *ifp, u32 cmd, void *data, u32 len) 158 { 159 s32 err; 160 161 mutex_lock(&ifp->drvr->proto_block); 162 err = brcmf_fil_cmd_data(ifp, cmd, data, len, false); 163 164 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, len=%d\n", ifp->ifidx, cmd, len); 165 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 166 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 167 168 mutex_unlock(&ifp->drvr->proto_block); 169 170 return err; 171 } 172 173 174 s32 175 brcmf_fil_cmd_int_set(struct brcmf_if *ifp, u32 cmd, u32 data) 176 { 177 s32 err; 178 __le32 data_le = cpu_to_le32(data); 179 180 mutex_lock(&ifp->drvr->proto_block); 181 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, data); 182 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), true); 183 mutex_unlock(&ifp->drvr->proto_block); 184 185 return err; 186 } 187 188 s32 189 brcmf_fil_cmd_int_get(struct brcmf_if *ifp, u32 cmd, u32 *data) 190 { 191 s32 err; 192 __le32 data_le = cpu_to_le32(*data); 193 194 mutex_lock(&ifp->drvr->proto_block); 195 err = brcmf_fil_cmd_data(ifp, cmd, &data_le, sizeof(data_le), false); 196 mutex_unlock(&ifp->drvr->proto_block); 197 *data = le32_to_cpu(data_le); 198 brcmf_dbg(FIL, "ifidx=%d, cmd=%d, value=%d\n", ifp->ifidx, cmd, *data); 199 200 return err; 201 } 202 203 static u32 204 brcmf_create_iovar(char *name, const char *data, u32 datalen, 205 char *buf, u32 buflen) 206 { 207 u32 len; 208 209 len = strlen(name) + 1; 210 211 if ((len + datalen) > buflen) 212 return 0; 213 214 memcpy(buf, name, len); 215 216 /* append data onto the end of the name string */ 217 if (data && datalen) 218 memcpy(&buf[len], data, datalen); 219 220 return len + datalen; 221 } 222 223 224 s32 225 brcmf_fil_iovar_data_set(struct brcmf_if *ifp, char *name, const void *data, 226 u32 len) 227 { 228 struct brcmf_pub *drvr = ifp->drvr; 229 s32 err; 230 u32 buflen; 231 232 mutex_lock(&drvr->proto_block); 233 234 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); 235 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 236 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 237 238 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 239 sizeof(drvr->proto_buf)); 240 if (buflen) { 241 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 242 buflen, true); 243 } else { 244 err = -EPERM; 245 brcmf_err("Creating iovar failed\n"); 246 } 247 248 mutex_unlock(&drvr->proto_block); 249 return err; 250 } 251 252 s32 253 brcmf_fil_iovar_data_get(struct brcmf_if *ifp, char *name, void *data, 254 u32 len) 255 { 256 struct brcmf_pub *drvr = ifp->drvr; 257 s32 err; 258 u32 buflen; 259 260 mutex_lock(&drvr->proto_block); 261 262 buflen = brcmf_create_iovar(name, data, len, drvr->proto_buf, 263 sizeof(drvr->proto_buf)); 264 if (buflen) { 265 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 266 buflen, false); 267 if (err == 0) 268 memcpy(data, drvr->proto_buf, len); 269 } else { 270 err = -EPERM; 271 brcmf_err("Creating iovar failed\n"); 272 } 273 274 brcmf_dbg(FIL, "ifidx=%d, name=%s, len=%d\n", ifp->ifidx, name, len); 275 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 276 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 277 278 mutex_unlock(&drvr->proto_block); 279 return err; 280 } 281 282 s32 283 brcmf_fil_iovar_int_set(struct brcmf_if *ifp, char *name, u32 data) 284 { 285 __le32 data_le = cpu_to_le32(data); 286 287 return brcmf_fil_iovar_data_set(ifp, name, &data_le, sizeof(data_le)); 288 } 289 290 s32 291 brcmf_fil_iovar_int_get(struct brcmf_if *ifp, char *name, u32 *data) 292 { 293 __le32 data_le = cpu_to_le32(*data); 294 s32 err; 295 296 err = brcmf_fil_iovar_data_get(ifp, name, &data_le, sizeof(data_le)); 297 if (err == 0) 298 *data = le32_to_cpu(data_le); 299 return err; 300 } 301 302 static u32 303 brcmf_create_bsscfg(s32 bsscfgidx, char *name, char *data, u32 datalen, 304 char *buf, u32 buflen) 305 { 306 const s8 *prefix = "bsscfg:"; 307 s8 *p; 308 u32 prefixlen; 309 u32 namelen; 310 u32 iolen; 311 __le32 bsscfgidx_le; 312 313 if (bsscfgidx == 0) 314 return brcmf_create_iovar(name, data, datalen, buf, buflen); 315 316 prefixlen = strlen(prefix); 317 namelen = strlen(name) + 1; /* lengh of iovar name + null */ 318 iolen = prefixlen + namelen + sizeof(bsscfgidx_le) + datalen; 319 320 if (buflen < iolen) { 321 brcmf_err("buffer is too short\n"); 322 return 0; 323 } 324 325 p = buf; 326 327 /* copy prefix, no null */ 328 memcpy(p, prefix, prefixlen); 329 p += prefixlen; 330 331 /* copy iovar name including null */ 332 memcpy(p, name, namelen); 333 p += namelen; 334 335 /* bss config index as first data */ 336 bsscfgidx_le = cpu_to_le32(bsscfgidx); 337 memcpy(p, &bsscfgidx_le, sizeof(bsscfgidx_le)); 338 p += sizeof(bsscfgidx_le); 339 340 /* parameter buffer follows */ 341 if (datalen) 342 memcpy(p, data, datalen); 343 344 return iolen; 345 } 346 347 s32 348 brcmf_fil_bsscfg_data_set(struct brcmf_if *ifp, char *name, 349 void *data, u32 len) 350 { 351 struct brcmf_pub *drvr = ifp->drvr; 352 s32 err; 353 u32 buflen; 354 355 mutex_lock(&drvr->proto_block); 356 357 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, 358 ifp->bsscfgidx, name, len); 359 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 360 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 361 362 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 363 drvr->proto_buf, sizeof(drvr->proto_buf)); 364 if (buflen) { 365 err = brcmf_fil_cmd_data(ifp, BRCMF_C_SET_VAR, drvr->proto_buf, 366 buflen, true); 367 } else { 368 err = -EPERM; 369 brcmf_err("Creating bsscfg failed\n"); 370 } 371 372 mutex_unlock(&drvr->proto_block); 373 return err; 374 } 375 376 s32 377 brcmf_fil_bsscfg_data_get(struct brcmf_if *ifp, char *name, 378 void *data, u32 len) 379 { 380 struct brcmf_pub *drvr = ifp->drvr; 381 s32 err; 382 u32 buflen; 383 384 mutex_lock(&drvr->proto_block); 385 386 buflen = brcmf_create_bsscfg(ifp->bsscfgidx, name, data, len, 387 drvr->proto_buf, sizeof(drvr->proto_buf)); 388 if (buflen) { 389 err = brcmf_fil_cmd_data(ifp, BRCMF_C_GET_VAR, drvr->proto_buf, 390 buflen, false); 391 if (err == 0) 392 memcpy(data, drvr->proto_buf, len); 393 } else { 394 err = -EPERM; 395 brcmf_err("Creating bsscfg failed\n"); 396 } 397 brcmf_dbg(FIL, "ifidx=%d, bsscfgidx=%d, name=%s, len=%d\n", ifp->ifidx, 398 ifp->bsscfgidx, name, len); 399 brcmf_dbg_hex_dump(BRCMF_FIL_ON(), data, 400 min_t(uint, len, MAX_HEX_DUMP_LEN), "data\n"); 401 402 mutex_unlock(&drvr->proto_block); 403 return err; 404 405 } 406 407 s32 408 brcmf_fil_bsscfg_int_set(struct brcmf_if *ifp, char *name, u32 data) 409 { 410 __le32 data_le = cpu_to_le32(data); 411 412 return brcmf_fil_bsscfg_data_set(ifp, name, &data_le, 413 sizeof(data_le)); 414 } 415 416 s32 417 brcmf_fil_bsscfg_int_get(struct brcmf_if *ifp, char *name, u32 *data) 418 { 419 __le32 data_le = cpu_to_le32(*data); 420 s32 err; 421 422 err = brcmf_fil_bsscfg_data_get(ifp, name, &data_le, 423 sizeof(data_le)); 424 if (err == 0) 425 *data = le32_to_cpu(data_le); 426 return err; 427 } 428