From bf1dec110d517755e66125d4b8699f50b8e6dfcf Mon Sep 17 00:00:00 2001 From: Manel Caro Date: Thu, 7 Mar 2019 20:13:19 +0100 Subject: Calibrator and uim Initial Commit --- Makefile | 52 ++ calibrator.c | 539 +++++++++++++++ calibrator.h | 157 +++++ ini.c | 1773 +++++++++++++++++++++++++++++++++++++++++++++++++ ini.h | 359 ++++++++++ misc_cmds.c | 323 +++++++++ nl80211.h | 1434 +++++++++++++++++++++++++++++++++++++++ nvs.c | 1283 +++++++++++++++++++++++++++++++++++ nvs.h | 31 + plt.c | 1431 +++++++++++++++++++++++++++++++++++++++ plt.h | 412 ++++++++++++ uim_rfkill/readme.txt | 25 + uim_rfkill/uim.c | 924 ++++++++++++++++++++++++++ uim_rfkill/uim.h | 154 +++++ wl18xx_plt.c | 787 ++++++++++++++++++++++ wl18xx_plt.h | 252 +++++++ 16 files changed, 9936 insertions(+) create mode 100644 Makefile create mode 100644 calibrator.c create mode 100644 calibrator.h create mode 100644 ini.c create mode 100644 ini.h create mode 100644 misc_cmds.c create mode 100644 nl80211.h create mode 100644 nvs.c create mode 100644 nvs.h create mode 100644 plt.c create mode 100644 plt.h create mode 100644 uim_rfkill/readme.txt create mode 100644 uim_rfkill/uim.c create mode 100644 uim_rfkill/uim.h create mode 100644 wl18xx_plt.c create mode 100644 wl18xx_plt.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd9ab98 --- /dev/null +++ b/Makefile @@ -0,0 +1,52 @@ +# Requisites: +# sudo apt-get install libnl-3-dev:armhf +# sudo apt install libnl-3-200:armhf +# sudo apt install libnl-genl-3-200:armhf +# last check, if linker fails then you should create +# this link manually: +# /lib/arm-linux-gnueabihf/libnl-genl-3.so -> libnl-genl-3.so.200.22.0 + + +CC = $(CROSS_COMPILE)gcc +CFLAGS = -O2 -Wall +CFLAGS += -I/usr/include -I/usr/include/libnl3 + +ifdef NLROOT +CFLAGS += -I${NLROOT} +endif + +LDFLAGS += -L/lib/arm-linux-gnueabihf/ +LIBS += -lm + +# Ubuntu 16.04 +NLVER = 3 + +ifeq ($(NLVER),3) + CFLAGS+=-DCONFIG_LIBNL32 + LIBS += -lnl-3 -lnl-genl-3 +else + CFLAGS+=-DCONFIG_LIBNL20 + LIBS += -lnl -lnl-genl +endif + +OBJS = nvs.o misc_cmds.o calibrator.o plt.o wl18xx_plt.o ini.o + +%.o: %.c calibrator.h nl80211.h plt.h nvs_dual_band.h + $(CC) $(CFLAGS) -c -o $@ $< + +all: $(OBJS) + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) -o calibrator + +uim: + $(CC) $(CFLAGS) $(LDFLAGS) uim_rfkill/$@.c -o $@ + +static: $(OBJS) + $(CC) $(LDFLAGS) --static $(OBJS) $(LIBS) -o calibrator + +install: + @echo Copy files to $(NFSROOT)/usr/bin + @cp -f ./calibrator $(NFSROOT)/usr/bin + @chmod 755 $(NFSROOT)/usr/bin/calibrator + +clean: + @rm -f *.o calibrator uim diff --git a/calibrator.c b/calibrator.c new file mode 100644 index 0000000..b8f66fa --- /dev/null +++ b/calibrator.c @@ -0,0 +1,539 @@ +/* + * PLT utility for wireless chip supported by TI's driver wl12xx + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "nl80211.h" +#include "calibrator.h" +#include "plt.h" +#include "ini.h" + +char calibrator_version[] = "0.80"; +#if !defined CONFIG_LIBNL20 && !defined CONFIG_LIBNL32 +/* libnl 2.0 compatibility code */ +static inline struct nl_handle *nl_socket_alloc(void) +{ + return nl_handle_alloc(); +} + +static inline void nl_socket_free(struct nl_sock *h) +{ + nl_handle_destroy(h); +} + +static inline int __genl_ctrl_alloc_cache(struct nl_sock *h, + struct nl_cache **cache) +{ + struct nl_cache *tmp = genl_ctrl_alloc_cache(h); + if (!tmp) + return -ENOMEM; + *cache = tmp; + return 0; +} +#define genl_ctrl_alloc_cache __genl_ctrl_alloc_cache +#endif /* CONFIG_LIBNL20 */ + +int calibrator_debug; + +static int nl80211_init(struct nl80211_state *state) +{ + int err; + + state->nl_sock = nl_socket_alloc(); + if (!state->nl_sock) { + fprintf(stderr, "Failed to allocate netlink socket.\n"); + return -ENOMEM; + } + + if (genl_connect(state->nl_sock)) { + fprintf(stderr, "Failed to connect to generic netlink.\n"); + err = -ENOLINK; + goto out_handle_destroy; + } + + if (genl_ctrl_alloc_cache(state->nl_sock, &state->nl_cache)) { + fprintf(stderr, "Failed to allocate generic netlink cache.\n"); + err = -ENOMEM; + goto out_handle_destroy; + } + + state->nl80211 = genl_ctrl_search_by_name(state->nl_cache, "nl80211"); + if (!state->nl80211) { + fprintf(stderr, "nl80211 not found.\n"); + err = -ENOENT; + goto out_cache_free; + } + + return 0; + + out_cache_free: + nl_cache_free(state->nl_cache); + out_handle_destroy: + nl_socket_free(state->nl_sock); + return err; +} + +static void nl80211_cleanup(struct nl80211_state *state) +{ + genl_family_put(state->nl80211); + nl_cache_free(state->nl_cache); + nl_socket_free(state->nl_sock); +} + +static int cmd_size; + +extern struct cmd __start___cmd; +extern struct cmd __stop___cmd; + +#define for_each_cmd(_cmd) \ + for (_cmd = &__start___cmd; _cmd < &__stop___cmd; \ + _cmd = (const struct cmd *)((char *)_cmd + cmd_size)) + + +static void __usage_cmd(const struct cmd *cmd, char *indent, bool full) +{ + const char *start, *lend, *end; + + printf("%s", indent); + + switch (cmd->idby) { + case CIB_NONE: + break; + case CIB_PHY: + printf("phy "); + break; + case CIB_NETDEV: + printf("dev "); + break; + } + if (cmd->parent && cmd->parent->name) + printf("%s ", cmd->parent->name); + printf("%s", cmd->name); + if (cmd->args) + printf(" %s", cmd->args); + printf("\n"); + + if (!full || !cmd->help) + return; + + /* hack */ + if (strlen(indent)) + indent = "\t\t"; + else + printf("\n"); + + /* print line by line */ + start = cmd->help; + end = strchr(start, '\0'); + do { + lend = strchr(start, '\n'); + if (!lend) + lend = end; + printf("%s", indent); + printf("%.*s\n", (int)(lend - start), start); + start = lend + 1; + } while (end != lend); + + printf("\n"); +} + +static void usage_options(void) +{ + printf("Options:\n"); + printf("\t--debug\t\tenable netlink debugging\n"); +} + +static const char *argv0; + +static void usage(bool full) +{ + const struct cmd *section, *cmd; + + printf("Usage:\t%s [options] command\n", argv0); + usage_options(); + printf("\t--version\tshow version (%s)\n", calibrator_version); + printf("Commands:\n"); + for_each_cmd(section) { + if (section->parent) + continue; + + if (section->handler && !section->hidden) + __usage_cmd(section, "\t", full); + + for_each_cmd(cmd) { + if (section != cmd->parent) + continue; + if (!cmd->handler || cmd->hidden) + continue; + __usage_cmd(cmd, "\t", full); + } + } +#if 0 + printf("\nYou can omit the 'phy' or 'dev' if " + "the identification is unique,\n" + "e.g. \"iw wlan0 info\" or \"iw phy0 info\". " + "(Don't when scripting.)\n\n" + "Do NOT screenscrape this tool, we don't " + "consider its output stable.\n\n"); +#endif +} + +static int print_help(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv) +{ + exit(3); +} +TOPLEVEL(help, NULL, 0, 0, CIB_NONE, print_help, + "Print usage for each command."); + +static void usage_cmd(const struct cmd *cmd) +{ + printf("\nUsage:\t%s [options] ", argv0); + __usage_cmd(cmd, "", true); + usage_options(); +} + +static void version(void) +{ + printf("calibrator version %s\n", calibrator_version); +} + +static int phy_lookup(char *name) +{ + char buf[200]; + int fd, pos; + + snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", name); + + fd = open(buf, O_RDONLY); + if (fd < 0) + return -1; + pos = read(fd, buf, sizeof(buf) - 1); + if (pos < 0) { + close(fd); + return -1; + } + buf[pos] = '\0'; + close(fd); + return atoi(buf); +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + + return NL_SKIP; +} + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + + return NL_STOP; +} + +static int __handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv, const struct cmd **cmdout) +{ + const struct cmd *cmd, *match = NULL, *sectcmd; + struct nl_cb *cb; + struct nl_msg *msg; + int devidx = 0; + int err, o_argc; + const char *command, *section; + char *tmp, **o_argv; + enum command_identify_by command_idby = CIB_NONE; +#if 0 + if (file_exist(CURRENT_NVS_NAME) < 0) { + fprintf(stderr, "\n\tUnable to find NVS file (%s).\n\t" + "Make sure to use reference-nvs.bin instead.\n\n", + CURRENT_NVS_NAME); + return 2; + } +#endif + if (argc <= 1) + return 1; + + o_argc = argc; + o_argv = argv; + + switch (idby) { + case II_PHY_IDX: + command_idby = CIB_PHY; + devidx = strtoul(*argv + 4, &tmp, 0); + if (*tmp != '\0') + return 1; + argc--; + argv++; + break; + case II_PHY_NAME: + command_idby = CIB_PHY; + devidx = phy_lookup(*argv); + argc--; + argv++; + break; + case II_NETDEV: + command_idby = CIB_NETDEV; + devidx = if_nametoindex(*argv); + if (devidx == 0) + devidx = -1; + argc--; + argv++; + break; + default: + break; + } + + if (devidx < 0) + return -errno; + + section = *argv; + argc--; + argv++; + + for_each_cmd(sectcmd) { + if (sectcmd->parent) + continue; + /* ok ... bit of a hack for the dupe 'info' section */ + if (match && sectcmd->idby != command_idby) + continue; + + if (strcmp(sectcmd->name, section) == 0) + match = sectcmd; + } + + sectcmd = match; + match = NULL; + if (!sectcmd) + return 1; + + if (argc > 0) { + command = *argv; + + for_each_cmd(cmd) { + if (!cmd->handler) + continue; + if (cmd->parent != sectcmd) + continue; + if (cmd->idby != command_idby) + continue; + if (strcmp(cmd->name, command)) + continue; + if (argc > 1 && !cmd->args) + continue; + match = cmd; + break; + } + + if (match) { + argc--; + argv++; + } + } + + + if (match) + cmd = match; + else { + /* Use the section itself, if possible. */ + cmd = sectcmd; + if (argc && !cmd->args) + return 1; + if (cmd->idby != command_idby) + return 1; + if (!cmd->handler) + return 1; + } + + if (cmdout) + *cmdout = cmd; + + if (!cmd->cmd) { + argc = o_argc; + argv = o_argv; + return cmd->handler(state, NULL, NULL, argc, argv); + } + + msg = nlmsg_alloc(); + if (!msg) { + fprintf(stderr, "failed to allocate netlink message\n"); + return 2; + } + + cb = nl_cb_alloc(calibrator_debug ? NL_CB_DEBUG : NL_CB_DEFAULT); + if (!cb) { + fprintf(stderr, "failed to allocate netlink callbacks\n"); + err = 2; + goto out_free_msg; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(state->nl80211), 0, + cmd->nl_msg_flags, cmd->cmd, 0); + + switch (command_idby) { + case CIB_PHY: + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, devidx); + break; + case CIB_NETDEV: + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, devidx); + break; + default: + break; + } + + err = cmd->handler(state, cb, msg, argc, argv); + if (err) { + fprintf(stderr, "failed to handle\n"); + goto out; + } + + err = nl_send_auto_complete(state->nl_sock, msg); + if (err < 0) { + fprintf(stderr, "failed to autocomplete\n"); + goto out; + } + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + while (err > 0) + nl_recvmsgs(state->nl_sock, cb); + + out: + nl_cb_put(cb); + out_free_msg: + nlmsg_free(msg); + return err; + + nla_put_failure: + fprintf(stderr, "building message failed\n"); + return 2; +} + +int handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv) +{ + return __handle_cmd(state, idby, argc, argv, NULL); +} + +int main(int argc, char **argv) +{ + struct nl80211_state nlstate; + int err; + const struct cmd *cmd = NULL; + + /* calculate command size including padding */ + cmd_size = abs((long)&__section_set - (long)&__section_get); + /* strip off self */ + argc--; + argv0 = *argv++; + + if (argc > 0 && strcmp(*argv, "--debug") == 0) { + calibrator_debug = 1; + argc--; + argv++; + } + + if (argc > 0 && ((strcmp(*argv, "--version") == 0) || + (strcmp(*argv, "-v") == 0))) { + version(); + return 0; + } + + /* need to treat "help" command specially so it works w/o nl80211 */ + if (argc == 0 || strcmp(*argv, "help") == 0) { + usage(argc != 0); + return 0; + } + + err = nl80211_init(&nlstate); + if (err) + return 1; + + if (strcmp(*argv, "dev") == 0 && argc > 1) { + argc--; + argv++; + err = __handle_cmd(&nlstate, II_NETDEV, argc, argv, &cmd); + } else if (strncmp(*argv, "phy", 3) == 0 && argc > 1) { + if (strlen(*argv) == 3) { + argc--; + argv++; + err = __handle_cmd(&nlstate, II_PHY_NAME, + argc, argv, &cmd); + } else if (*(*argv + 3) == '#') + err = __handle_cmd(&nlstate, II_PHY_IDX, + argc, argv, &cmd); + else + goto detect; + } else { + int idx; + enum id_input idby = II_NONE; + detect: + idx = if_nametoindex(argv[0]); + if (idx != 0) + idby = II_NETDEV; + else { + idx = phy_lookup(argv[0]); + if (idx >= 0) + idby = II_PHY_NAME; + } + + err = __handle_cmd(&nlstate, idby, argc, argv, &cmd); + } + + if (err == 1) { + if (cmd) + usage_cmd(cmd); + else + usage(false); + } else if (err < 0) + fprintf(stderr, "command failed: %s (%d)\n", + strerror(-err), err); + + nl80211_cleanup(&nlstate); + + return err; +} + +void str2mac(unsigned char *pmac, char *pch) +{ + int i; + + for (i = 0; i < MAC_ADDR_LEN; i++) { + pmac[i] = (unsigned char)strtoul(pch, &pch, 16); + pch++; + } +} diff --git a/calibrator.h b/calibrator.h new file mode 100644 index 0000000..5daf96a --- /dev/null +++ b/calibrator.h @@ -0,0 +1,157 @@ +#ifndef __CALIBRATOR_H +#define __CALIBRATOR_H + +#include +#include +#include +#include +#include + +#include "nl80211.h" + +#define ETH_ALEN 6 + +#if !defined CONFIG_LIBNL20 && !defined CONFIG_LIBNL32 +# define nl_sock nl_handle +#endif + +struct nl80211_state { + struct nl_sock *nl_sock; + struct nl_cache *nl_cache; + struct genl_family *nl80211; +}; + +enum command_identify_by { + CIB_NONE, + CIB_PHY, + CIB_NETDEV, +}; + +enum id_input { + II_NONE, + II_NETDEV, + II_PHY_NAME, + II_PHY_IDX, +}; + +struct cmd { + const char *name; + const char *args; + const char *help; + const enum nl80211_commands cmd; + int nl_msg_flags; + int hidden; + const enum command_identify_by idby; + /* + * The handler should return a negative error code, + * zero on success, 1 if the arguments were wrong + * and the usage message should and 2 otherwise. + */ + int (*handler)(struct nl80211_state *state, + struct nl_cb *cb, + struct nl_msg *msg, + int argc, char **argv); + const struct cmd *parent; +}; + +#define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0])) + +#define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help)\ + static struct cmd \ + __cmd ## _ ## _symname ## _ ## _handler ## _ ## _nlcmd ## _ ## _idby ## _ ## _hidden\ + __attribute__((used)) __attribute__((section("__cmd"))) = { \ + .name = (_name), \ + .args = (_args), \ + .cmd = (_nlcmd), \ + .nl_msg_flags = (_flags), \ + .hidden = (_hidden), \ + .idby = (_idby), \ + .handler = (_handler), \ + .help = (_help), \ + .parent = _section, \ + } +#define COMMAND(section, name, args, cmd, flags, idby, handler, help) \ + __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 0, idby, handler, help) +#define HIDDEN(section, name, args, cmd, flags, idby, handler) \ + __COMMAND(&(__section ## _ ## section), name, #name, args, cmd, flags, 1, idby, handler, NULL) + +#define TOPLEVEL(_name, _args, _nlcmd, _flags, _idby, _handler, _help) \ + struct cmd \ + __section ## _ ## _name \ + __attribute__((used)) __attribute__((section("__cmd"))) = { \ + .name = (#_name), \ + .args = (_args), \ + .cmd = (_nlcmd), \ + .nl_msg_flags = (_flags), \ + .idby = (_idby), \ + .handler = (_handler), \ + .help = (_help), \ + } +#define SECTION(_name) \ + struct cmd __section ## _ ## _name \ + __attribute__((used)) __attribute__((section("__cmd"))) = { \ + .name = (#_name), \ + .hidden = 1, \ + } + +#define DECLARE_SECTION(_name) \ + extern struct cmd __section ## _ ## _name; + +extern int calibrator_debug; + +int handle_cmd(struct nl80211_state *state, enum id_input idby, + int argc, char **argv); + +struct print_event_args { + bool frame, time; +}; + +__u32 listen_events(struct nl80211_state *state, + const int n_waits, const __u32 *waits); +__u32 __listen_events(struct nl80211_state *state, + const int n_waits, const __u32 *waits, + struct print_event_args *args); + + +int mac_addr_a2n(unsigned char *mac_addr, char *arg); +void mac_addr_n2a(char *mac_addr, unsigned char *arg); + +int parse_keys(struct nl_msg *msg, char **argv, int argc); + +void print_ht_mcs(const __u8 *mcs); +void print_ampdu_length(__u8 exponent); +void print_ampdu_spacing(__u8 spacing); +void print_ht_capability(__u16 cap); + +const char *iftype_name(enum nl80211_iftype iftype); +const char *command_name(enum nl80211_commands cmd); +int ieee80211_channel_to_frequency(int chan); +int ieee80211_frequency_to_channel(int freq); + +void print_ssid_escaped(const uint8_t len, const uint8_t *data); + +int nl_get_multicast_id(struct nl_sock *sock, const char *family, + const char *group); + +char *reg_initiator_to_string(__u8 initiator); + +const char *get_reason_str(uint16_t reason); +const char *get_status_str(uint16_t status); + +enum print_ie_type { + PRINT_SCAN, + PRINT_LINK, +}; + +#define BIT(x) (1ULL<<(x)) + +void print_ies(unsigned char *ie, int ielen, bool unknown, + enum print_ie_type ptype); + +void str2mac(unsigned char *pmac, char *pch); + +DECLARE_SECTION(set); +DECLARE_SECTION(get); +DECLARE_SECTION(plt); + +#endif /* __CALIBRATOR_H */ diff --git a/ini.c b/ini.c new file mode 100644 index 0000000..46cd8c7 --- /dev/null +++ b/ini.c @@ -0,0 +1,1773 @@ + + +/* + * PLT utility for wireless chip supported by TI's driver wl12xx + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "nl80211.h" + +#include "calibrator.h" +#include "plt.h" +#include "ini.h" +#include "nvs.h" + +static char *ini_get_line(char *s, int size, FILE *stream, int *line, + char **_pos) +{ + char *pos, *end, *sstart; + + while (fgets(s, size, stream)) { + s[size - 1] = '\0'; + pos = s; + + /* Skip white space from the beginning of line. */ + while (*pos == ' ' || *pos == '\t' || *pos == '\r') + pos++; + + /* Skip comment lines and empty lines */ + if (*pos == '#' || *pos == '\n' || *pos == '\0') + continue; + + /* + * Remove # comments unless they are within a double quoted + * string. + */ + sstart = strchr(pos, '"'); + if (sstart) + sstart = strrchr(sstart + 1, '"'); + if (!sstart) + sstart = pos; + end = strchr(sstart, '#'); + if (end) + *end-- = '\0'; + else + end = pos + strlen(pos) - 1; + + /* Remove trailing white space. */ + while (end > pos && + (*end == '\n' || *end == ' ' || *end == '\t' || + *end == '\r')) + *end-- = '\0'; + + if (*pos == '\0') + continue; + + (*line)++; + + if (_pos) + *_pos = pos; + return pos; + } + + if (_pos) + *_pos = NULL; + + return NULL; +} + +static int split_line(char *line, char **name, char **value) +{ + char *pos = line; + + *value = strchr(pos, '='); + if (!*value) { + fprintf(stderr, "Wrong format of line\n"); + return 1; + } + + *name = *value; + + (*name)--; + while (**name == ' ' || **name == '\t' || **name == '\r') + (*name)--; + + *++(*name) = '\0'; + + (*value)++; + while (**value == ' ' || **value == '\t' || **value == '\r') + (*value)++; + + return 0; +} + +/* Parse array of unsigned chars */ +static int parse_uc_a(char* name, char *val, unsigned char *out, size_t exp_size) +{ + size_t i = 0; + long v; + char *endval; + + while (*val) { + /* Advance to next token */ + while (*val == ' ' || *val == ',') + val++; + + if (i >= exp_size) { + fprintf(stderr, "Too many params for %s\n", name); + return 1; + } + v = strtol(val, &endval, 16); + + if (endval == val) { + fprintf(stderr, "Syntax error parsing %s\n", name); + return 1; + } + if (v > (long) UCHAR_MAX) { + fprintf(stderr, "Overflow parsing %s\n", name); + return 1; + } + out[i++] = v; + val = endval; + } + + if(exp_size != i) { + fprintf(stderr, "Too few parameters for %s\n", name); + return 1; + } + + return 0; +} + +/* Parse array of __le16 */ +static int parse_ui_a(char* name, char *val, __le16 *out, size_t exp_size) +{ + size_t i = 0; + long v; + char *endval; + unsigned char *ptr = (unsigned char *)out; + + while (*val) { + /* Advance to next token */ + while (*val == ' ' || *val == ',') + val++; + + if (i >= exp_size) { + fprintf(stderr, "Too many params for %s\n", name); + return 1; + } + v = strtol(val, &endval, 16); + + if (endval == val) { + fprintf(stderr, "Syntax error parsing %s\n", name); + return 1; + } + if (v > (long) INT16_MAX) { + fprintf(stderr, "Overflow parsing %s\n", name); + return 1; + } + ptr[i*2] = (unsigned char) v; + ptr[(i*2)+1] = (unsigned char) (v >> 8); + i++; + + val = endval; + } + + if(exp_size != i) { + fprintf(stderr, "Too few parameters for %s\n", name); + return 1; + } + + return 0; +} + +#define COMPARE_N_ADD(temp, str, val, ptr) \ + if (strncmp(temp, str, sizeof(temp)) == 0) { \ + return parse_uc_a(temp, val, (unsigned char*) ptr, sizeof(*ptr)); \ + } + +#define COMPARE_N_ADD2(temp, str, val, ptr) \ + if (strncmp(temp, str, sizeof(temp)) == 0) { \ + return parse_ui_a(temp, val, (__le16*) ptr, sizeof(*ptr)/2); \ + } + +static int parse_general_prms(char *l, struct wl12xx_common *cmn, + struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_general_params *gp = &(p->ini1271.general_params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("TXBiPFEMAutoDetect", l, val, + &gp->tx_bip_fem_auto_detect); + + cmn->auto_fem = gp->tx_bip_fem_auto_detect; + + COMPARE_N_ADD("TXBiPFEMManufacturer", l, val, + &gp->tx_bip_fem_manufacturer); + + COMPARE_N_ADD("RefClk", l, val, &gp->ref_clock); + + COMPARE_N_ADD("SettlingTime", l, val, &gp->settling_time); + + COMPARE_N_ADD("ClockValidOnWakeup", l, val, + &gp->clk_valid_on_wakeup); + + COMPARE_N_ADD("DC2DCMode", l, val, &gp->dc2dc_mode); + + COMPARE_N_ADD("Single_Dual_Band_Solution", l, val, + &gp->dual_mode_select); + + COMPARE_N_ADD("Settings", l, val, &gp->general_settings); + + COMPARE_N_ADD("SRState", l, val, &gp->sr_state); + + COMPARE_N_ADD("SRF1", l, val, &gp->srf1); + + COMPARE_N_ADD("SRF2", l, val, &gp->srf2); + + COMPARE_N_ADD("SRF3", l, val, &gp->srf3); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_general_prms_128x(char *l, struct wl12xx_common *cmn, + struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_general_params *gp = + &(p->ini128x.general_params); + + if (split_line(l, &name, &val)) + return 1; + + + COMPARE_N_ADD("TXBiPFEMAutoDetect", l, val, + &gp->tx_bip_fem_auto_detect); + + COMPARE_N_ADD("TXBiPFEMManufacturer", l, val, + &gp->tx_bip_fem_manufacturer); + + cmn->auto_fem = gp->tx_bip_fem_auto_detect; + + COMPARE_N_ADD("RefClk", l, val, &gp->ref_clock); + + COMPARE_N_ADD("SettlingTime", l, val, &gp->settling_time); + + COMPARE_N_ADD("ClockValidOnWakeup", l, val, &gp->clk_valid_on_wakeup); + + COMPARE_N_ADD("TCXO_Clk", l, val, &gp->tcxo_ref_clock); + + COMPARE_N_ADD("TCXO_SettlingTime", l, val, &gp->tcxo_settling_time); + + COMPARE_N_ADD("TCXO_ClockValidOnWakeup", l, val, + &gp->tcxo_valid_on_wakeup); + + COMPARE_N_ADD("TCXO_LDO_Voltage", l, val, &gp->tcxo_ldo_voltage); + + COMPARE_N_ADD("Platform_configuration", l, val, &gp->platform_conf); + + COMPARE_N_ADD("Single_Dual_Band_Solution", l, val, + &gp->dual_mode_select); + + COMPARE_N_ADD("Settings", l, val, &gp->general_settings); + + COMPARE_N_ADD("XTALItrimVal", l, val, &gp->xtal_itrim_val); + + COMPARE_N_ADD("SRState", l, val, &gp->sr_state); + + COMPARE_N_ADD("SRF1", l, val, &gp->srf1); + + COMPARE_N_ADD("SRF2", l, val, &gp->srf2); + + COMPARE_N_ADD("SRF3", l, val, &gp->srf3); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_band2_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_band_params_2 *gp = + &(p->ini1271.stat_radio_params_2); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("RxTraceInsertionLoss_2_4G", l, val, + &gp->rx_trace_insertion_loss); + + COMPARE_N_ADD("TXTraceLoss_2_4G", l, val, + &gp->tx_trace_loss); + + COMPARE_N_ADD("RxRssiAndProcessCompensation_2_4G", l, val, + &gp->rx_rssi_process_compens); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_band2_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_band_params_2 *gp = &(p->ini128x.stat_radio_params_2); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("RxTraceInsertionLoss_2_4G", l, val, + &gp->rx_trace_insertion_loss); + + COMPARE_N_ADD("TxTraceLoss_2_4G", l, val, &gp->tx_trace_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_band5_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_band_params_5 *gp = + &(p->ini1271.stat_radio_params_5); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("RxTraceInsertionLoss_5G", l, val, + &gp->rx_trace_insertion_loss); + + COMPARE_N_ADD("TXTraceLoss_5G", l, val, + &gp->tx_trace_loss); + + COMPARE_N_ADD("RxRssiAndProcessCompensation_5G", l, val, + &gp->rx_rssi_process_compens); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_band5_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_band_params_5 *gp = &(p->ini128x.stat_radio_params_5); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("RxTraceInsertionLoss_5G", l, val, + &gp->rx_trace_insertion_loss); + + COMPARE_N_ADD("TxTraceLoss_5G", l, val, + &gp->tx_trace_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem0_band2_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_2 *gp = + &(p->ini1271.dyn_radio_params_2[0].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM0_TXBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM0_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM0_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM0_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM0_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem0_band2_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_2 *gp = + &(p->ini128x.dyn_radio_params_2[0].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM0_TxBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM0_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM0_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM0_TxPDVsChannelOffsets_2_4G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM0_TxPDVsTemperature_2_4G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM0_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM0_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem1_band2_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_2 *gp = + &(p->ini1271.dyn_radio_params_2[1].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM1_TXBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM1_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM1_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM1_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM1_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem1_band2_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_2 *gp = + &(p->ini128x.dyn_radio_params_2[1].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM1_TxBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM1_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM1_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM1_TxPDVsChannelOffsets_2_4G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM1_TxPDVsTemperature_2_4G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM1_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM1_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem2_band2_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_2 *gp = + &(p->ini1271.dyn_radio_params_2[2].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM2_TXBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM2_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM2_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM2_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM2_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM2_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM2_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM2_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem2_band2_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_2 *gp = + &(p->ini128x.dyn_radio_params_2[2].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM2_TxBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM2_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM2_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM2_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM2_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM2_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM2_TxPDVsChannelOffsets_2_4G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM2_TxPDVsTemperature_2_4G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM2_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM2_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem3_band2_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_2 *gp = + &(p->ini1271.dyn_radio_params_2[3].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM3_TXBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM3_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM3_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM3_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM3_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM3_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM3_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM3_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem3_band2_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_2 *gp = + &(p->ini128x.dyn_radio_params_2[3].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM3_TxBiPReferencePDvoltage_2_4G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM3_TxBiPReferencePower_2_4G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM3_TxBiPOffsetdB_2_4G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_2_4G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM3_DegradedLowToNormalThr_2_4G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM3_NormalToDegradedHighThr_2_4G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_2_4G_11b", l, val, + &gp->tx_per_chan_pwr_limits_11b); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_2_4G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM3_TxPDVsRateOffsets_2_4G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM3_TxPDVsChannelOffsets_2_4G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM3_TxPDVsTemperature_2_4G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM3_TxIbiasTable_2_4G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM3_RxFemInsertionLoss_2_4G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem0_band5_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_5 *gp = + &(p->ini1271.dyn_radio_params_5[0].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM0_TXBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM0_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM0_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM0_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM0_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem0_band5_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_5 *gp = + &(p->ini128x.dyn_radio_params_5[0].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM0_TxBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM0_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM0_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM0_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM0_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM0_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM0_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM0_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM0_TxPDVsChannelOffsets_5G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM0_TxPDVsTemperature_5G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM0_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM0_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem1_band5_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_5 *gp = + &(p->ini1271.dyn_radio_params_5[1].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM1_TXBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM1_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM1_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM1_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM1_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem1_band5_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_5 *gp = + &(p->ini128x.dyn_radio_params_5[1].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM1_TxBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM1_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM1_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM1_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM1_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM1_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM1_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM1_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM1_TxPDVsChannelOffsets_5G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM1_TxPDVsTemperature_5G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM1_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM1_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem2_band5_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_5 *gp = + &(p->ini1271.dyn_radio_params_5[2].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM2_TXBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM2_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM2_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM2_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM2_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM2_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM2_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM2_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem2_band5_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_5 *gp = + &(p->ini128x.dyn_radio_params_5[2].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM2_TxBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM2_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM2_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM2_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM2_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM2_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM2_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM2_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM2_TxPDVsChannelOffsets_5G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM2_TxPDVsTemperature_5G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM2_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM2_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem3_band5_prms(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl1271_ini_fem_params_5 *gp = + &(p->ini1271.dyn_radio_params_5[3].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM3_TXBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM3_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM3_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM3_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM3_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM3_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM3_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM3_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem3_band5_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini_fem_params_5 *gp = + &(p->ini128x.dyn_radio_params_5[3].params); + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD2("FEM3_TxBiPReferencePDvoltage_5G", l, val, + &gp->tx_bip_ref_pd_voltage); + + COMPARE_N_ADD("FEM3_TxBiPReferencePower_5G", l, val, + &gp->tx_bip_ref_power); + + COMPARE_N_ADD("FEM3_TxBiPOffsetdB_5G", l, val, + &gp->tx_bip_ref_offset); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Normal", l, val, + &gp->tx_per_rate_pwr_limits_normal); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Degraded", l, val, + &gp->tx_per_rate_pwr_limits_degraded); + + COMPARE_N_ADD("FEM3_TxPerRatePowerLimits_5G_Extreme", l, val, + &gp->tx_per_rate_pwr_limits_extreme); + + COMPARE_N_ADD("FEM3_DegradedLowToNormalThr_5G", l, val, + &gp->degraded_low_to_normal_thr); + + COMPARE_N_ADD("FEM3_NormalToDegradedHighThr_5G", l, val, + &gp->normal_to_degraded_high_thr); + + COMPARE_N_ADD("FEM3_TxPerChannelPowerLimits_5G_OFDM", l, val, + &gp->tx_per_chan_pwr_limits_ofdm); + + COMPARE_N_ADD("FEM3_TxPDVsRateOffsets_5G", l, val, + &gp->tx_pd_vs_rate_offsets); + + COMPARE_N_ADD("FEM3_TxPDVsChannelOffsets_5G", l, val, + &gp->tx_pd_vs_chan_offsets); + + COMPARE_N_ADD("FEM3_TxPDVsTemperature_5G", l, val, + &gp->tx_pd_vs_temperature); + + COMPARE_N_ADD("FEM3_TxIbiasTable_5G", l, val, + &gp->tx_ibias); + + COMPARE_N_ADD("FEM3_RxFemInsertionLoss_5G", l, val, + &gp->rx_fem_insertion_loss); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int parse_fem_prms_128x(char *l, struct wl12xx_ini *p) +{ + char *name, *val; + struct wl128x_ini *gp = &p->ini128x; + + if (split_line(l, &name, &val)) + return 1; + + COMPARE_N_ADD("FemVendorAndOptions", l, val, + &gp->fem_vendor_and_options); + + fprintf(stderr, "Unable to parse: (%s)\n", l); + + return 1; +} + +static int find_section(const char *l, enum wl1271_ini_section *st, int *cntr, + struct wl12xx_common *cmn) +{ + enum wl12xx_arch arch = cmn->arch; + if (strncmp("TXBiPFEMAutoDetect", l, 18) == 0) { + *st = GENERAL_PRMS; + if (arch == WL128X_ARCH) + *cntr = 17; + else + *cntr = 12; + + return 0; + } + + if (strncmp("RxTraceInsertionLoss_2_4G", l, 25) == 0) { + *st = BAND2_PRMS; + if (arch == WL128X_ARCH) + *cntr = 2; + else + *cntr = 3; + + return 0; + } + + if (strncmp("FemVendorAndOptions", l, 19) == 0) { + *st = FEM_PRMS; + *cntr = 1; + return 0; + } + + if (strncmp("RxTraceInsertionLoss_5G", l, 23) == 0) { + *st = BAND5_PRMS; + if (arch == WL128X_ARCH) + *cntr = 2; + else + *cntr = 3; + + return 0; + } + + if (strncmp("FEM0_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 || + strncmp("FEM0_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) { + *st = FEM0_BAND2_PRMS; + cmn->fem0_bands++; + if (arch == WL128X_ARCH) + *cntr = 15; + else + *cntr = 13; + + return 0; + } + + if (strncmp("FEM1_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 || + strncmp("FEM1_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) { + *st = FEM1_BAND2_PRMS; + cmn->fem1_bands++; + if (arch == WL128X_ARCH) + *cntr = 15; + else + *cntr = 13; + + return 0; + } + + if (strncmp("FEM2_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 || + strncmp("FEM2_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) { + *st = FEM2_BAND2_PRMS; + cmn->fem2_bands++; + if (arch == WL128X_ARCH) + *cntr = 15; + else + *cntr = 13; + + return 0; + } + + if (strncmp("FEM3_TXBiPReferencePDvoltage_2_4G", l, 33) == 0 || + strncmp("FEM3_TxBiPReferencePDvoltage_2_4G", l, 33) == 0) { + *st = FEM3_BAND2_PRMS; + cmn->fem3_bands++; + if (arch == WL128X_ARCH) + *cntr = 15; + else + *cntr = 13; + + return 0; + } + + if (strncmp("FEM0_TXBiPReferencePDvoltage_5G", l, 31) == 0 || + strncmp("FEM0_TxBiPReferencePDvoltage_5G", l, 31) == 0) { + *st = FEM0_BAND5_PRMS; + cmn->fem0_bands++; + if (arch == WL128X_ARCH) + *cntr = 14; + else + *cntr = 12; + + return 0; + } + + if (strncmp("FEM1_TXBiPReferencePDvoltage_5G", l, 31) == 0 || + strncmp("FEM1_TxBiPReferencePDvoltage_5G", l, 31) == 0) { + *st = FEM1_BAND5_PRMS; + cmn->fem1_bands++; + if (arch == WL128X_ARCH) + *cntr = 14; + else + *cntr = 12; + + return 0; + } + + if (strncmp("FEM2_TXBiPReferencePDvoltage_5G", l, 31) == 0 || + strncmp("FEM2_TxBiPReferencePDvoltage_5G", l, 31) == 0) { + *st = FEM2_BAND5_PRMS; + cmn->fem2_bands++; + if (arch == WL128X_ARCH) + *cntr = 14; + else + *cntr = 12; + + return 0; + } + + if (strncmp("FEM3_TXBiPReferencePDvoltage_5G", l, 31) == 0 || + strncmp("FEM3_TxBiPReferencePDvoltage_5G", l, 31) == 0) { + *st = FEM3_BAND5_PRMS; + cmn->fem3_bands++; + if (arch == WL128X_ARCH) + *cntr = 14; + else + *cntr = 12; + + return 0; + } + + return 1; +} + +static const char* ini_section_str(enum wl1271_ini_section section) +{ + const char *section_str; + + switch(section) { + + case GENERAL_PRMS: + section_str = "GENERAL_PARAMS"; + break; + + case FEM_PRMS: + section_str = "FEM_PARAMS"; + break; + + case BAND2_PRMS: + section_str = "BAND2_PARAMS"; + break; + + case BAND5_PRMS: + section_str = "BAND5_PARAMS"; + break; + + case FEM0_BAND2_PRMS: + section_str = "FEM0_BAND2_PARAMS"; + break; + + case FEM1_BAND2_PRMS: + section_str = "FEM1_BAND2_PARAMS"; + break; + + case FEM2_BAND2_PRMS: + section_str = "FEM2_BAND2_PARAMS"; + break; + + case FEM3_BAND2_PRMS: + section_str = "FEM3_BAND2_PARAMS"; + break; + + case FEM0_BAND5_PRMS: + section_str = "FEM0_BAND5_PARAMS"; + break; + + case FEM1_BAND5_PRMS: + section_str = "FEM1_BAND5_PARAMS"; + break; + + case FEM2_BAND5_PRMS: + section_str = "FEM2_BAND5_PARAMS"; + break; + + case FEM3_BAND5_PRMS: + section_str = "FEM3_BAND5_PARAMS"; + break; + + case UKNOWN_SECTION: + default: + section_str = "UNKNOWN_SECTION"; + break; + } + + return section_str; +} + +static int ini_parse_line(char *l, struct wl12xx_common *cmn) +{ + static enum wl1271_ini_section status; + static int cntr; + + if (cntr) { + /* + * Recovery mode - + * Check if didn't finish current section and we are already in + * another one. This can happen in case of optional params in section. + */ + enum wl1271_ini_section curr_section = status; + if (!find_section(l, &status, &cntr, cmn)) { + printf("Some params missing in ini section %s moving to section %s\n", + ini_section_str(curr_section), + ini_section_str(status)); + } + } + + if (!cntr && find_section(l, &status, &cntr, cmn)) { + fprintf(stderr, "Uknown ini section %s\n", l); + return 1; + } + + switch (status) { + case GENERAL_PRMS: /* general parameters */ + cntr--; + return cmn->parse_ops->prs_general_prms(l, cmn, &cmn->ini); + case FEM_PRMS: /* FEM parameters */ + if (cmn->arch == WL1271_ARCH) { + fprintf(stderr, "The parameter not from 127x architecture\n"); + return 1; + } + cntr--; + return parse_fem_prms_128x(l, &cmn->ini); + case BAND2_PRMS: /* band 2.4GHz parameters */ + cntr--; + return cmn->parse_ops->prs_band2_prms(l, &cmn->ini); + case BAND5_PRMS: /* band 5GHz parameters */ + cntr--; + return cmn->parse_ops->prs_band5_prms(l, &cmn->ini); + case FEM0_BAND2_PRMS: /* FEM0 band 2.4GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem0_band2_prms(l, &cmn->ini); + case FEM1_BAND2_PRMS: /* FEM1 band 2.4GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem1_band2_prms(l, &cmn->ini); + case FEM2_BAND2_PRMS: /* FEM2 band 2.4GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem2_band2_prms(l, &cmn->ini); + case FEM3_BAND2_PRMS: /* FEM3 band 2.4GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem3_band2_prms(l, &cmn->ini); + case FEM0_BAND5_PRMS: /* FEM0 band 5GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem0_band5_prms(l, &cmn->ini); + case FEM1_BAND5_PRMS: /* FEM1 band 5GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem1_band5_prms(l, &cmn->ini); + case FEM2_BAND5_PRMS: /* FEM2 band 5GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem2_band5_prms(l, &cmn->ini); + case FEM3_BAND5_PRMS: /* FEM3 band 5GHz parameters */ + cntr--; + return cmn->parse_ops->prs_fem3_band5_prms(l, &cmn->ini); + case UKNOWN_SECTION: + /* added because of compilation warning. handeled in find_section() */ + break; + } + + return 1; +} + +#if 0 +static void ini_dump(struct wl1271_ini *ini) +{ + int i; + + printf("\n"); + printf("General params:\n"); + printf("ref clock: %02X\n", + ini->general_params.ref_clock); + printf("settling time: %02X\n", + ini->general_params.settling_time); + printf("clk valid on wakeup: %02X\n", + ini->general_params.clk_valid_on_wakeup); + printf("dc2dc mode: %02X\n", + ini->general_params.dc2dc_mode); + printf("dual band mode: %02X\n", + ini->general_params.dual_mode_select); + printf("tx bip fem auto detect: %02X\n", + ini->general_params.tx_bip_fem_auto_detect); + printf("tx bip fem manufacturer: %02X\n", + ini->general_params.tx_bip_fem_manufacturer); + printf("general settings: %02X\n", + ini->general_params.general_settings); + printf("sr state: %02X\n", + ini->general_params.sr_state); + + printf("srf1:"); + for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++) + printf(" %02X", ini->general_params.srf1[i]); + printf("\n"); + + printf("srf2:"); + for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++) + printf(" %02X", ini->general_params.srf2[i]); + printf("\n"); + + printf("srf3:"); + for (i = 0; i < WL1271_INI_MAX_SMART_REFLEX_PARAM; i++) + printf(" %02X", ini->general_params.srf3[i]); + printf("\n"); + + printf("Static 2.4 band params:\n"); + + printf("rx trace insertion loss: %02X\n", + ini->stat_radio_params_2.rx_trace_insertion_loss); + + printf("rx rssi n process compensation:"); + for (i = 0; i < WL1271_INI_RSSI_PROCESS_COMPENS_SIZE; i++) + printf(" %02X", + ini->stat_radio_params_2.rx_rssi_process_compens[i]); + printf("\n"); + + printf("tx trace: %02X\n", + ini->stat_radio_params_2.tx_trace_loss); + + printf("Dynamic 2.4 band params for FEM\n"); + + printf("Static 5 band params:\n"); + + printf("rx trace insertion loss:"); + for (i = 0; i < WL1271_INI_SUB_BAND_COUNT_5; i++) + printf(" %02X", + ini->stat_radio_params_5.rx_rssi_process_compens[i]); + printf("\n"); + + printf("rx rssi n process compensation:"); + for (i = 0; i < WL1271_INI_RSSI_PROCESS_COMPENS_SIZE; i++) + printf(" %02X", + ini->stat_radio_params_5.rx_rssi_process_compens[i]); + printf("\n"); + + printf("tx trace:"); + for (i = 0; i < WL1271_INI_SUB_BAND_COUNT_5; i++) + printf(" %02X", + ini->stat_radio_params_5.tx_trace_loss[i]); + printf("\n"); + + printf("Dynamic 5 band params for FEM\n"); + +} +#endif + + +static int is_dual_mode(struct wl12xx_ini *p) +{ + struct wl1271_ini_general_params *gp = &(p->ini1271.general_params); + return gp->dual_mode_select; +} + +static int is_dual_mode_128x(struct wl12xx_ini *p) +{ + struct wl128x_ini_general_params *gp = &(p->ini128x.general_params); + return gp->dual_mode_select; +} + + +static struct wl12xx_parse_ops wl1271_parse_ops = { + .prs_general_prms = parse_general_prms, + .prs_band2_prms = parse_band2_prms, + .prs_band5_prms = parse_band5_prms, + .prs_fem0_band2_prms = parse_fem0_band2_prms, + .prs_fem1_band2_prms = parse_fem1_band2_prms, + .prs_fem2_band2_prms = parse_fem2_band2_prms, + .prs_fem3_band2_prms = parse_fem3_band2_prms, + .prs_fem0_band5_prms = parse_fem0_band5_prms, + .prs_fem1_band5_prms = parse_fem1_band5_prms, + .prs_fem2_band5_prms = parse_fem2_band5_prms, + .prs_fem3_band5_prms = parse_fem3_band5_prms, + .is_dual_mode = is_dual_mode, +}; + +static struct wl12xx_parse_ops wl128x_parse_ops = { + .prs_general_prms = parse_general_prms_128x, + .prs_band2_prms = parse_band2_prms_128x, + .prs_band5_prms = parse_band5_prms_128x, + .prs_fem0_band2_prms = parse_fem0_band2_prms_128x, + .prs_fem1_band2_prms = parse_fem1_band2_prms_128x, + .prs_fem2_band2_prms = parse_fem2_band2_prms_128x, + .prs_fem3_band2_prms = parse_fem3_band2_prms_128x, + .prs_fem0_band5_prms = parse_fem0_band5_prms_128x, + .prs_fem1_band5_prms = parse_fem1_band5_prms_128x, + .prs_fem2_band5_prms = parse_fem2_band5_prms_128x, + .prs_fem3_band5_prms = parse_fem3_band5_prms_128x, + .is_dual_mode = is_dual_mode_128x, +}; + +int ini_get_dual_mode(struct wl12xx_common *cmn) +{ + return cmn->parse_ops->is_dual_mode(&cmn->ini); +} + +int nvs_get_arch(int file_size, struct wl12xx_common *cmn) +{ + enum wl12xx_arch arch = UNKNOWN_ARCH; + + switch (file_size) { + case WL127X_NVS_FILE_SZ: + arch = WL1271_ARCH; + cmn->parse_ops = &wl1271_parse_ops; + break; + case WL128X_NVS_FILE_SZ: + arch = WL128X_ARCH; + cmn->parse_ops = &wl128x_parse_ops; + break; + } + + if (cmn->arch != UNKNOWN_ARCH && cmn->arch != arch) { + cmn->parse_ops = NULL; + return 1; + } + + cmn->arch = arch; + + return 0; +} + +static int ini_get_arch(FILE *f, struct wl12xx_common *cmn) +{ + char buf[1024], *pos; + int line = 0; + enum wl12xx_arch arch = UNKNOWN_ARCH; + + while (ini_get_line(buf, sizeof(buf), f, &line, &pos)) { + if (strncmp("TCXO_Clk", pos, 8) == 0) { + arch = WL128X_ARCH; + break; + } + } + + if (arch == UNKNOWN_ARCH) + arch = WL1271_ARCH; + + if (cmn->arch != UNKNOWN_ARCH && cmn->arch != arch) + return 1; + + cmn->arch = arch; + + if (cmn->arch == WL1271_ARCH) + cmn->parse_ops = &wl1271_parse_ops; + else + cmn->parse_ops = &wl128x_parse_ops; + + fseek(f, 0L, SEEK_SET); + + return 0; +} + +int read_ini(const char *filename, struct wl12xx_common *cmn) +{ + FILE *f; + char buf[1024], *pos; + int ret = 0, line = 0; + + cmn->auto_fem = 0; + cmn->fem0_bands = 0; + cmn->fem1_bands = 0; + cmn->fem2_bands = 0; + cmn->fem3_bands = 0; + + f = fopen(filename, "r"); + if (f == NULL) { + fprintf(stderr, "Unable to open file %s (%s)\n", + filename, strerror(errno)); + return 1; + } + + /* check if it 127x or 128x */ + if (ini_get_arch(f, cmn)) { + fprintf(stderr, "Unable to define wireless architecture\n"); + ret = 1; + goto out; + } + + /* start parsing */ + while (ini_get_line(buf, sizeof(buf), f, &line, &pos)) { + ret = ini_parse_line(pos, cmn); + if (ret) break; + } + +out: + fclose(f); +#if 0 + ini_dump(ini); +#endif + return ret; +} diff --git a/ini.h b/ini.h new file mode 100644 index 0000000..27f95a1 --- /dev/null +++ b/ini.h @@ -0,0 +1,359 @@ +/* + * This file is part of wl1271 + * + * Copyright (C) 2010 Nokia Corporation + * + * Contact: Luciano Coelho + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __INI_H__ +#define __INI_H__ + +#include + +#define WL1271_INI_MAX_SMART_REFLEX_PARAM 16 + +struct wl1271_ini_general_params { + unsigned char ref_clock; + unsigned char settling_time; + unsigned char clk_valid_on_wakeup; + unsigned char dc2dc_mode; + unsigned char dual_mode_select; + unsigned char tx_bip_fem_auto_detect; + unsigned char tx_bip_fem_manufacturer; + unsigned char general_settings; + unsigned char sr_state; + unsigned char srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM]; + unsigned char srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM]; + unsigned char srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; +} __attribute__((packed)); + +#define WL128X_INI_MAX_SETTINGS_PARAM 4 + +struct wl128x_ini_general_params { + unsigned char ref_clock; + unsigned char settling_time; + unsigned char clk_valid_on_wakeup; + unsigned char tcxo_ref_clock; + unsigned char tcxo_settling_time; + unsigned char tcxo_valid_on_wakeup; + unsigned char tcxo_ldo_voltage; + unsigned char xtal_itrim_val; + unsigned char platform_conf; + unsigned char dual_mode_select; + unsigned char tx_bip_fem_auto_detect; + unsigned char tx_bip_fem_manufacturer; + unsigned char general_settings[WL128X_INI_MAX_SETTINGS_PARAM]; + unsigned char sr_state; + unsigned char srf1[WL1271_INI_MAX_SMART_REFLEX_PARAM]; + unsigned char srf2[WL1271_INI_MAX_SMART_REFLEX_PARAM]; + unsigned char srf3[WL1271_INI_MAX_SMART_REFLEX_PARAM]; +} __attribute__((packed)); + +#define WL1271_INI_RSSI_PROCESS_COMPENS_SIZE 15 + +struct wl1271_ini_band_params_2 { + unsigned char rx_trace_insertion_loss; + unsigned char tx_trace_loss; + unsigned char + rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; +} __attribute__((packed)); + +#define WL1271_INI_CHANNEL_COUNT_2 14 + +struct wl128x_ini_band_params_2 { + unsigned char rx_trace_insertion_loss; + unsigned char tx_trace_loss[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char + rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; +} __attribute__((packed)); + +#define WL1271_INI_RATE_GROUP_COUNT 6 + +struct wl1271_ini_fem_params_2 { + __le16 tx_bip_ref_pd_voltage; + unsigned char tx_bip_ref_power; + unsigned char tx_bip_ref_offset; + unsigned char + tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char tx_ibias[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char rx_fem_insertion_loss; + unsigned char degraded_low_to_normal_thr; + unsigned char normal_to_degraded_high_thr; +} __attribute__((packed)); + +#define WL128X_INI_RATE_GROUP_COUNT 7 +/* low and high temperatures*/ +#define WL128X_INI_PD_VS_TEMPERATURE_RANGES 2 + +struct wl128x_ini_fem_params_2 { + __le16 tx_bip_ref_pd_voltage; + unsigned char tx_bip_ref_power; + unsigned char tx_bip_ref_offset; + unsigned char + tx_per_rate_pwr_limits_normal [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_degraded [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_extreme [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char tx_per_chan_pwr_limits_11b[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT]; + unsigned char tx_ibias[WL128X_INI_RATE_GROUP_COUNT + 1]; + unsigned char tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_2]; + unsigned char tx_pd_vs_temperature[WL128X_INI_PD_VS_TEMPERATURE_RANGES]; + unsigned char rx_fem_insertion_loss; + unsigned char degraded_low_to_normal_thr; + unsigned char normal_to_degraded_high_thr; +} __attribute__((packed)); + +#define WL1271_INI_CHANNEL_COUNT_5 35 +#define WL1271_INI_SUB_BAND_COUNT_5 7 + +struct wl1271_ini_band_params_5 { + unsigned char rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_trace_loss[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char + rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; +} __attribute__((packed)); + +struct wl128x_ini_band_params_5 { + unsigned char rx_trace_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_trace_loss[WL1271_INI_CHANNEL_COUNT_5]; + unsigned char + rx_rssi_process_compens[WL1271_INI_RSSI_PROCESS_COMPENS_SIZE]; +} __attribute__((packed)); + +struct wl1271_ini_fem_params_5 { + __le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char + tx_per_rate_pwr_limits_normal[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_degraded[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_extreme[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5]; + unsigned char tx_pd_vs_rate_offsets[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char tx_ibias[WL1271_INI_RATE_GROUP_COUNT]; + unsigned char rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char degraded_low_to_normal_thr; + unsigned char normal_to_degraded_high_thr; +} __attribute__((packed)); + +struct wl128x_ini_fem_params_5 { + __le16 tx_bip_ref_pd_voltage[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_bip_ref_power[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char tx_bip_ref_offset[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char + tx_per_rate_pwr_limits_normal [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_degraded [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char + tx_per_rate_pwr_limits_extreme [WL128X_INI_RATE_GROUP_COUNT]; + unsigned char tx_per_chan_pwr_limits_ofdm[WL1271_INI_CHANNEL_COUNT_5]; + unsigned char tx_pd_vs_rate_offsets[WL128X_INI_RATE_GROUP_COUNT]; + unsigned char tx_ibias[WL128X_INI_RATE_GROUP_COUNT]; + unsigned char tx_pd_vs_chan_offsets[WL1271_INI_CHANNEL_COUNT_5]; + unsigned char tx_pd_vs_temperature[WL1271_INI_SUB_BAND_COUNT_5 * + WL128X_INI_PD_VS_TEMPERATURE_RANGES]; + unsigned char rx_fem_insertion_loss[WL1271_INI_SUB_BAND_COUNT_5]; + unsigned char degraded_low_to_normal_thr; + unsigned char normal_to_degraded_high_thr; +} __attribute__((packed)); + +/* NVS data structure */ +#define WL1271_INI_NVS_SECTION_SIZE 468 + +/* We have four FEM module types: 0-RFMD, 1-TQS, 2-SKW, 3-TQS_HP */ +#define WL1271_INI_FEM_MODULE_COUNT 4 + +#define WL1271_INI_LEGACY_NVS_FILE_SIZE 800 +/* + * In NVS we only store two FEM module entries - + * FEM modules 0,2,3 are stored in entry 0 + * FEM module 1 is stored in entry 1 + */ +#define WL12XX_NVS_FEM_MODULE_COUNT 2 + +#define WL12XX_FEM_TO_NVS_ENTRY(ini_fem_module) \ + ((ini_fem_module) == 1 ? 1 : 0) + +struct wl1271_nvs_ini { + struct wl1271_ini_general_params general_params; + unsigned char padding1; + struct wl1271_ini_band_params_2 stat_radio_params_2; + unsigned char padding2; + struct { + struct wl1271_ini_fem_params_2 params; + unsigned char padding; + } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; + struct wl1271_ini_band_params_5 stat_radio_params_5; + unsigned char padding3; + struct { + struct wl1271_ini_fem_params_5 params; + unsigned char padding; + } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; +} __attribute__((packed)); + +struct wl1271_nvs_file { + /* NVS section */ + unsigned char nvs[WL1271_INI_NVS_SECTION_SIZE]; + /* INI section */ + struct wl1271_nvs_ini ini; +} __attribute__((packed)); + +struct wl128x_nvs_ini { + struct wl128x_ini_general_params general_params; + unsigned char fem_vendor_and_options; + struct wl128x_ini_band_params_2 stat_radio_params_2; + unsigned char padding2; + struct { + struct wl128x_ini_fem_params_2 params; + unsigned char padding; + } dyn_radio_params_2[WL12XX_NVS_FEM_MODULE_COUNT]; + struct wl128x_ini_band_params_5 stat_radio_params_5; + unsigned char padding3; + struct { + struct wl128x_ini_fem_params_5 params; + unsigned char padding; + } dyn_radio_params_5[WL12XX_NVS_FEM_MODULE_COUNT]; +} __attribute__((packed)); + +struct wl128x_nvs_file { + /* NVS section */ + unsigned char nvs[WL1271_INI_NVS_SECTION_SIZE]; + /* INI section */ + struct wl128x_nvs_ini ini; +} __attribute__((packed)); + +struct wl1271_ini { + struct wl1271_ini_general_params general_params; + unsigned char padding1; + struct wl1271_ini_band_params_2 stat_radio_params_2; + unsigned char padding2; + struct { + struct wl1271_ini_fem_params_2 params; + unsigned char padding; + } dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT]; + struct wl1271_ini_band_params_5 stat_radio_params_5; + unsigned char padding3; + struct { + struct wl1271_ini_fem_params_5 params; + unsigned char padding; + } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; +} __attribute__((packed)); + +struct wl128x_ini { + struct wl128x_ini_general_params general_params; + unsigned char fem_vendor_and_options; + struct wl128x_ini_band_params_2 stat_radio_params_2; + unsigned char padding2; + struct { + struct wl128x_ini_fem_params_2 params; + unsigned char padding; + } dyn_radio_params_2[WL1271_INI_FEM_MODULE_COUNT]; + struct wl128x_ini_band_params_5 stat_radio_params_5; + unsigned char padding3; + struct { + struct wl128x_ini_fem_params_5 params; + unsigned char padding; + } dyn_radio_params_5[WL1271_INI_FEM_MODULE_COUNT]; +} __attribute__((packed)); + +enum wl1271_ini_section { + UKNOWN_SECTION, + GENERAL_PRMS, + FEM_PRMS, + BAND2_PRMS, + BAND5_PRMS, + FEM0_BAND2_PRMS, + FEM1_BAND2_PRMS, + FEM2_BAND2_PRMS, + FEM3_BAND2_PRMS, + FEM0_BAND5_PRMS, + FEM1_BAND5_PRMS, + FEM2_BAND5_PRMS, + FEM3_BAND5_PRMS +}; + +enum wl12xx_arch { + UNKNOWN_ARCH, + WL1271_ARCH = 0x0403, + WL128X_ARCH = 0x0503 +}; + +struct wl12xx_ini { + union { + struct wl1271_ini ini1271; + struct wl128x_ini ini128x; + }; +}; + +#define DUAL_MODE_UNSET 0xff + +struct wl12xx_common { + enum wl12xx_arch arch; + unsigned char auto_fem; + unsigned int fem0_bands; + unsigned int fem1_bands; + unsigned int fem2_bands; + unsigned int fem3_bands; + struct wl12xx_parse_ops *parse_ops; + struct wl12xx_nvs_ops *nvs_ops; + struct wl12xx_ini ini; + char *nvs_name; +}; + +struct wl12xx_parse_ops { + int (*prs_general_prms)(char *l, struct wl12xx_common *cmn, + struct wl12xx_ini *p); + /* int (*prs_fem_prms)(char *l, void *gp); */ + int (*prs_band2_prms)(char *l, struct wl12xx_ini *p); + int (*prs_band5_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem0_band2_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem1_band2_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem2_band2_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem3_band2_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem0_band5_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem1_band5_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem2_band5_prms)(char *l, struct wl12xx_ini *p); + int (*prs_fem3_band5_prms)(char *l, struct wl12xx_ini *p); + int (*is_dual_mode)(struct wl12xx_ini *p); +}; + +struct wl12xx_nvs_ops { + int (*nvs_fill_radio_prms)(int fd, struct wl12xx_ini *p, char *buf); + int (*nvs_set_autofem)(int fd, char *buf, unsigned char val); + int (*nvs_set_fem_manuf)(int fd, char *buf, unsigned char val); +}; + +int nvs_get_arch(int file_size, struct wl12xx_common *cmn); + +int read_ini(const char *filename, struct wl12xx_common *cmn); + +int ini_get_dual_mode(struct wl12xx_common *cmn); +#endif diff --git a/misc_cmds.c b/misc_cmds.c new file mode 100644 index 0000000..8a2ebd0 --- /dev/null +++ b/misc_cmds.c @@ -0,0 +1,323 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "calibrator.h" +#include "plt.h" +#include "ini.h" +#include "nvs.h" + +SECTION(get); +SECTION(set); + +static int get_nvs_mac(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + unsigned char mac_buff[12]; + char *fname; + int fd; + + argc -= 2; + argv += 2; + + fname = get_opt_nvsinfile(argc, argv); + if (!fname) + return 1; + + fd = open(fname, O_RDONLY); + if (fd < 0) { + perror("Error opening file for reading"); + return 1; + } + + read(fd, mac_buff, 12); + + printf("MAC addr from NVS: %02x:%02x:%02x:%02x:%02x:%02x\n", + mac_buff[11], mac_buff[10], mac_buff[6], + mac_buff[5], mac_buff[4], mac_buff[3]); + + close(fd); + + return 0; +} + +COMMAND(get, nvs_mac, "[]", 0, 0, CIB_NONE, get_nvs_mac, + "Get MAC addr from NVS file (offline)"); + +/* + * Sets MAC address in NVS. + * The default value for MAC is random where 1 byte zero. + */ +static int set_nvs_mac(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + argc -= 2; + argv += 2; + + if ((argc != 2) || (strlen(argv[1]) != 17)) + return 1; + + nvs_set_mac(argv[0], argv[1]); + return 0; +} + +COMMAND(set, nvs_mac, " ", 0, 0, CIB_NONE, set_nvs_mac, + "Set MAC addr in NVS file (offline), like XX:XX:XX:XX:XX:XX"); + +static int set_ref_nvs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct wl12xx_common cmn = { + .arch = UNKNOWN_ARCH, + .parse_ops = NULL + }; + + argc -= 2; + argv += 2; + + if (argc < 1 || argc > 2) + return 1; + + if (read_ini(*argv, &cmn)) { + fprintf(stderr, "Fail to read ini file\n"); + return 1; + } + argv++; + argc--; + + cfg_nvs_ops(&cmn); + + cmn.nvs_name = get_opt_nvsoutfile(argc, argv); + if (create_nvs_file(&cmn)) { + fprintf(stderr, "Fail to create reference NVS file\n"); + return 1; + } + + printf("%04X\n", cmn.arch); + + return 0; +} + +COMMAND(set, ref_nvs, " []", 0, 0, CIB_NONE, set_ref_nvs, + "Create reference NVS file"); + +static int set_upd_nvs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *infname = NULL, *outfname = NULL; + struct wl12xx_common cmn = { + .arch = UNKNOWN_ARCH, + .parse_ops = NULL + }; + + argc -= 2; + argv += 2; + + if (argc < 1) + return 1; + + if (read_ini(*argv, &cmn)) { + fprintf(stderr, "Fail to read ini file\n"); + return 1; + } + argc--; + argv++; + + infname = get_opt_nvsinfile(argc, argv); + if (!infname) + return 1; + + if (argc) { + argc--; + argv++; + } + outfname = get_opt_nvsoutfile(argc, argv); + if (!outfname) + return 1; + + cfg_nvs_ops(&cmn); + + if (update_nvs_file(infname, outfname, &cmn)) { + fprintf(stderr, "Fail to update NVS file\n"); + return 1; + } +#if 0 + printf("\n\tThe updated NVS file (%s) is ready\n\tCopy it to %s and " + "reboot the system\n\n", NEW_NVS_NAME, CURRENT_NVS_NAME); +#endif + return 0; +} + +COMMAND(set, upd_nvs, " [] []", 0, 0, CIB_NONE, set_upd_nvs, + "Update values of a NVS from INI file"); + +static int get_dump_nvs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *fname = NULL; + + argc -= 2; + argv += 2; + + fname = get_opt_nvsinfile(argc, argv); + if (!fname) + return 1; + + if (dump_nvs_file(fname)) { + fprintf(stderr, "Fail to dump NVS file\n"); + return 1; + } + + return 0; +} + +COMMAND(get, dump_nvs, "[]", 0, 0, CIB_NONE, get_dump_nvs, + "Dump NVS file, specified by option or current"); + +static int get_info_nvs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *fname; + + argc -= 2; + argv += 2; + + fname = get_opt_nvsinfile(argc, argv); + if(!fname) + return 1; + + if (info_nvs_file(fname)) { + fprintf(stderr, "Fail to read info from NVS file\n"); + return 1; + } + + return 0; +} + +COMMAND(get, info_nvs, "[]", 0, 0, CIB_NONE, get_info_nvs, + "Print information from nvs file"); + +static int set_autofem(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *fname = NULL; + int res; + unsigned int val; + struct wl12xx_common cmn = { + .arch = UNKNOWN_ARCH, + .parse_ops = NULL + }; + + argc -= 2; + argv += 2; + + if (argc < 1) { + fprintf(stderr, "Missing argument\n"); + return 2; + } + + res = sscanf(argv[0], "%x", &val); + if (res != 1 || val > 1) { + fprintf(stderr, "Invalid argument\n"); + return 1; + } + argv++; + argc--; + + fname = get_opt_nvsinfile(argc, argv); + + if (set_nvs_file_autofem(fname, val, &cmn)) { + fprintf(stderr, "Fail to set AutoFEM\n"); + return 1; + } + + return 0; +} + +COMMAND(set, autofem, "<0-manual|1-auto> []", 0, 0, CIB_NONE, set_autofem, + "Set Auto FEM detection, where 0 - manual, 1 - auto detection"); + +static int set_fem_manuf(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *fname = NULL; + int res; + unsigned int val; + struct wl12xx_common cmn = { + .arch = UNKNOWN_ARCH, + .parse_ops = NULL + }; + + argc -= 2; + argv += 2; + + if (argc < 1) { + fprintf(stderr, "Missing argument\n"); + return 2; + } + res = sscanf(argv[0], "%x", &val); + if(res != 1 || val >= WL1271_INI_FEM_MODULE_COUNT) { + fprintf(stderr, "Invalid argument\n"); + return 1; + } + argv++; + argc--; + + fname = get_opt_nvsinfile(argc, argv); + + if (set_nvs_file_fem_manuf(fname, val, &cmn)) { + fprintf(stderr, "Fail to set AutoFEM\n"); + return 1; + } + + return 0; +} + +COMMAND(set, fem_manuf, "<0|1|2|3> []", 0, 0, CIB_NONE, set_fem_manuf, + "Set FEM manufacturer"); + +static int get_drv_info(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + argc -= 2; + argv += 2; + + if (argc < 1) { + fprintf(stderr, "Missing argument (device name)\n"); + return 2; + } + + return do_get_drv_info(argv[0], NULL, NULL); +} + +COMMAND(get, drv_info, "", 0, 0, CIB_NONE, get_drv_info, + "Get driver information: PG version"); + +static int get_hw_version(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + int ret, chip_id = 0; + + argc -= 2; + argv += 2; + + if (argc < 1) { + fprintf(stderr, "Missing argument (device name)\n"); + return 2; + } + + ret = do_get_drv_info(argv[0], &chip_id, NULL); + if (!ret) + printf("%08X\n", chip_id); + + return ret; +} + +COMMAND(get, hw_version, "", 0, 0, CIB_NONE, get_hw_version, + "Get HW version (chip id)"); + diff --git a/nl80211.h b/nl80211.h new file mode 100644 index 0000000..0096675 --- /dev/null +++ b/nl80211.h @@ -0,0 +1,1434 @@ +#ifndef __LINUX_NL80211_H +#define __LINUX_NL80211_H +/* + * 802.11 netlink interface public header + * + * Copyright 2006, 2007, 2008 Johannes Berg + * Copyright 2008 Michael Wu + * Copyright 2008 Luis Carlos Cobo + * Copyright 2008 Michael Buesch + * Copyright 2008, 2009 Luis R. Rodriguez + * Copyright 2008 Jouni Malinen + * Copyright 2008 Colin McCabe + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +#include + +/** + * DOC: Station handling + * + * Stations are added per interface, but a special case exists with VLAN + * interfaces. When a station is bound to an AP interface, it may be moved + * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN). + * The station is still assumed to belong to the AP interface it was added + * to. + * + * TODO: need more info? + */ + +/** + * enum nl80211_commands - supported nl80211 commands + * + * @NL80211_CMD_UNSPEC: unspecified command to catch errors + * + * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request + * to get a list of all present wiphys. + * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or + * %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME, + * %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ, + * %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT, + * %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + * and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD. + * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request + * or rename notification. Has attributes %NL80211_ATTR_WIPHY and + * %NL80211_ATTR_WIPHY_NAME. + * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME. + * + * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration; + * either a dump request on a %NL80211_ATTR_WIPHY or a specific get + * on an %NL80211_ATTR_IFINDEX is supported. + * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE. + * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response + * to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX, + * %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also + * be sent from userspace to request creation of a new virtual interface, + * then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and + * %NL80211_ATTR_IFNAME. + * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes + * %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from + * userspace to request deletion of a virtual interface, then requires + * attribute %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified + * by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC. + * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT, + * %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD. + * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA, + * %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER, + * and %NL80211_ATTR_KEY_SEQ attributes. + * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX + * or %NL80211_ATTR_MAC. + * + * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a + * %NL80222_CMD_NEW_BEACON message) + * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface + * using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD, + * %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes. + * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface, + * parameters are like for %NL80211_CMD_SET_BEACON. + * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it + * + * @NL80211_CMD_GET_STATION: Get station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_STATION: Set station attributes for station identified by + * %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all stations, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_MPATH: Set mesh path attributes for mesh path to + * destination %NL80211_ATTR_MAC on the interface identified by + * %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the + * the interface identified by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC + * or, if no MAC address given, all mesh paths, on the interface identified + * by %NL80211_ATTR_IFINDEX. + * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by + * %NL80211_ATTR_IFINDEX. + * + * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set + * regulatory domain. + * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command + * after being queried by the kernel. CRDA replies by sending a regulatory + * domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our + * current alpha2 if it found a match. It also provides + * NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each + * regulatory rule is a nested set of attributes given by + * %NL80211_ATTR_REG_RULE_FREQ_[START|END] and + * %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by + * %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and + * %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP. + * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain + * to the the specified ISO/IEC 3166-1 alpha2 country code. The core will + * store this as a valid request and then query userspace for it. + * + * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the + * interface identified by %NL80211_ATTR_IFINDEX + * + * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The + * interface is identified with %NL80211_ATTR_IFINDEX and the management + * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be + * added to the end of the specified management frame is specified with + * %NL80211_ATTR_IE. If the command succeeds, the requested data will be + * added to all specified management frames generated by + * kernel/firmware/driver. + * Note: This command has been removed and it is only reserved at this + * point to avoid re-using existing command number. The functionality this + * command was planned for has been provided with cleaner design with the + * option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN, + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE, + * NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE. + * + * @NL80211_CMD_GET_SCAN: get scan results + * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters + * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to + * NL80211_CMD_GET_SCAN and on the "scan" multicast group) + * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons, + * partial scan results may be available + * + * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation + * or noise level + * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to + * NL80211_CMD_GET_SURVEY and on the "scan" multicast group) + * + * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain + * has been changed and provides details of the request information + * that caused the change such as who initiated the regulatory request + * (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx + * (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if + * the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or + * %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain + * set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is + * %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on + * to (%NL80211_ATTR_REG_ALPHA2). + * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon + * has been found while world roaming thus enabling active scan or + * any mode of operation that initiates TX (beacons) on a channel + * where we would not have been able to do either before. As an example + * if you are world roaming (regulatory domain set to world or if your + * driver is using a custom world roaming regulatory domain) and while + * doing a passive scan on the 5 GHz band you find an AP there (if not + * on a DFS channel) you will now be able to actively scan for that AP + * or use AP mode on your card on that same channel. Note that this will + * never be used for channels 1-11 on the 2 GHz band as they are always + * enabled world wide. This beacon hint is only sent if your device had + * either disabled active scanning or beaconing on a channel. We send to + * userspace the wiphy on which we removed a restriction from + * (%NL80211_ATTR_WIPHY) and the channel on which this occurred + * before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER) + * the beacon hint was processed. + * + * @NL80211_CMD_AUTHENTICATE: authentication request and notification. + * This command is used both as a command (request to authenticate) and + * as an event on the "mlme" multicast group indicating completion of the + * authentication process. + * When used as a command, %NL80211_ATTR_IFINDEX is used to identify the + * interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and + * BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify + * the SSID (mainly for association, but is included in authentication + * request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used + * to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE + * is used to specify the authentication type. %NL80211_ATTR_IE is used to + * define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs) + * to be added to the frame. + * When used as an event, this reports reception of an Authentication + * frame in station and IBSS modes when the local MLME processed the + * frame, i.e., it was for the local STA and was received in correct + * state. This is similar to MLME-AUTHENTICATE.confirm primitive in the + * MLME SAP interface (kernel providing MLME, userspace SME). The + * included %NL80211_ATTR_FRAME attribute contains the management frame + * (including both the header and frame body, but not FCS). This event is + * also used to indicate if the authentication attempt timed out. In that + * case the %NL80211_ATTR_FRAME attribute is replaced with a + * %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which + * pending authentication timed out). + * @NL80211_CMD_ASSOCIATE: association request and notification; like + * NL80211_CMD_AUTHENTICATE but for Association and Reassociation + * (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request, + * MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives). + * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like + * NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to + * MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication + * primitives). + * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like + * NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to + * MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives). + * + * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael + * MIC (part of TKIP) failure; sent on the "mlme" multicast group; the + * event includes %NL80211_ATTR_MAC to describe the source MAC address of + * the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key + * type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and + * %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this + * event matches with MLME-MICHAELMICFAILURE.indication() primitive + * + * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a + * FREQ attribute (for the initial frequency if no peer can be found) + * and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those + * should be fixed rather than automatically determined. Can only be + * executed on a network interface that is UP, and fixed BSSID/FREQ + * may be rejected. Another optional parameter is the beacon interval, + * given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not + * given defaults to 100 TU (102.4ms). + * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is + * determined by the network interface. + * + * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute + * to identify the device, and the TESTDATA blob attribute to pass through + * to the driver. + * + * @NL80211_CMD_CONNECT: connection request and notification; this command + * requests to connect to a specified network but without separating + * auth and assoc steps. For this, you need to specify the SSID in a + * %NL80211_ATTR_SSID attribute, and can optionally specify the association + * IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC, + * %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT. + * It is also sent as an event, with the BSSID and response IEs when the + * connection is established or failed to be established. This can be + * determined by the STATUS_CODE attribute. + * @NL80211_CMD_ROAM: request that the card roam (currently not implemented), + * sent as an event when the card/driver roamed by itself. + * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify + * userspace that a connection was dropped by the AP or due to other + * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and + * %NL80211_ATTR_REASON_CODE attributes are used. + * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * + * @NL80211_CMD_MAX: highest used command number + * @__NL80211_CMD_AFTER_LAST: internal use + */ +enum nl80211_commands { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_CMD_UNSPEC, + + NL80211_CMD_GET_WIPHY, /* can dump */ + NL80211_CMD_SET_WIPHY, + NL80211_CMD_NEW_WIPHY, + NL80211_CMD_DEL_WIPHY, + + NL80211_CMD_GET_INTERFACE, /* can dump */ + NL80211_CMD_SET_INTERFACE, + NL80211_CMD_NEW_INTERFACE, + NL80211_CMD_DEL_INTERFACE, + + NL80211_CMD_GET_KEY, + NL80211_CMD_SET_KEY, + NL80211_CMD_NEW_KEY, + NL80211_CMD_DEL_KEY, + + NL80211_CMD_GET_BEACON, + NL80211_CMD_SET_BEACON, + NL80211_CMD_NEW_BEACON, + NL80211_CMD_DEL_BEACON, + + NL80211_CMD_GET_STATION, + NL80211_CMD_SET_STATION, + NL80211_CMD_NEW_STATION, + NL80211_CMD_DEL_STATION, + + NL80211_CMD_GET_MPATH, + NL80211_CMD_SET_MPATH, + NL80211_CMD_NEW_MPATH, + NL80211_CMD_DEL_MPATH, + + NL80211_CMD_SET_BSS, + + NL80211_CMD_SET_REG, + NL80211_CMD_REQ_SET_REG, + + NL80211_CMD_GET_MESH_PARAMS, + NL80211_CMD_SET_MESH_PARAMS, + + NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */, + + NL80211_CMD_GET_REG, + + NL80211_CMD_GET_SCAN, + NL80211_CMD_TRIGGER_SCAN, + NL80211_CMD_NEW_SCAN_RESULTS, + NL80211_CMD_SCAN_ABORTED, + + NL80211_CMD_REG_CHANGE, + + NL80211_CMD_AUTHENTICATE, + NL80211_CMD_ASSOCIATE, + NL80211_CMD_DEAUTHENTICATE, + NL80211_CMD_DISASSOCIATE, + + NL80211_CMD_MICHAEL_MIC_FAILURE, + + NL80211_CMD_REG_BEACON_HINT, + + NL80211_CMD_JOIN_IBSS, + NL80211_CMD_LEAVE_IBSS, + + NL80211_CMD_TESTMODE, + + NL80211_CMD_CONNECT, + NL80211_CMD_ROAM, + NL80211_CMD_DISCONNECT, + + NL80211_CMD_SET_WIPHY_NETNS, + + NL80211_CMD_GET_SURVEY, + NL80211_CMD_NEW_SURVEY_RESULTS, + + /* add new commands above here */ + + /* used to define NL80211_CMD_MAX below */ + __NL80211_CMD_AFTER_LAST, + NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1 +}; + +/* + * Allow user space programs to use #ifdef on new commands by defining them + * here + */ +#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS +#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE +#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE +#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE +#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE +#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE +#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE +#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT + +/** + * enum nl80211_attrs - nl80211 netlink attributes + * + * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors + * + * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf. + * /sys/class/ieee80211//index + * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming) + * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters + * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz + * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ + * if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included): + * NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including + * this attribute) + * NL80211_CHAN_HT20 = HT20 only + * NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel + * NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel + * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is + * less than or equal to the RTS threshold; allowed range: 1..255; + * dot11ShortRetryLimit; u8 + * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is + * greater than the RTS threshold; allowed range: 1..255; + * dot11ShortLongLimit; u8 + * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum + * length in octets for frames; allowed range: 256..8000, disable + * fragmentation with (u32)-1; dot11FragmentationThreshold; u32 + * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length + * larger than or equal to this use RTS/CTS handshake); allowed range: + * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * + * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on + * @NL80211_ATTR_IFNAME: network interface name + * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype + * + * @NL80211_ATTR_MAC: MAC address (various uses) + * + * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3) + * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * + * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU + * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing + * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE + * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE + * + * @NL80211_ATTR_STA_AID: Association ID for the station (u16) + * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2) + * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by + * IEEE 802.11 7.3.1.6 (u16). + * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported + * rates as defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station + * to, or the AP interface the station was originally added to to. + * @NL80211_ATTR_STA_INFO: information about a station, part of station info + * given for %NL80211_CMD_GET_STATION, nested attribute containing + * info as possible, see &enum nl80211_sta_info. + * + * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands, + * consisting of a nested array. + * + * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes). + * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link. + * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path. + * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path + * info given for %NL80211_CMD_GET_MPATH, nested attribute described at + * &enum nl80211_mpath_info. + * + * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of + * &enum nl80211_mntr_flags. + * + * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the + * current regulatory domain should be set to or is already set to. + * For example, 'CR', for Costa Rica. This attribute is used by the kernel + * to query the CRDA to retrieve one regulatory domain. This attribute can + * also be used by userspace to query the kernel for the currently set + * regulatory domain. We chose an alpha2 as that is also used by the + * IEEE-802.11d country information element to identify a country. + * Users can also simply ask the wireless core to set regulatory domain + * to a specific alpha2. + * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory + * rules. + * + * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled + * (u8, 0 or 1) + * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic + * rates in format defined by IEEE 802.11 7.3.2.2 but without the length + * restriction (at most %NL80211_MAX_SUPP_RATES). + * + * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from + * association request when used with NL80211_CMD_NEW_STATION) + * + * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all + * supported interface types, each a flag attribute with the number + * of the interface mode. + * + * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for + * %NL80211_CMD_SET_MGMT_EXTRA_IE. + * + * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with + * %NL80211_CMD_SET_MGMT_EXTRA_IE). + * + * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with + * a single scan request, a wiphy attribute. + * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements + * that can be added to a scan request + * + * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz) + * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive + * scanning and include a zero-length SSID (wildcard) for wildcard scan + * @NL80211_ATTR_BSS: scan result BSS + * + * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain + * currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_* + * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently + * set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*) + * + * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies + * an array of command numbers (i.e. a mapping index to command number) + * that the driver for the given wiphy supports. + * + * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header + * and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and + * NL80211_CMD_ASSOCIATE events + * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets) + * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type, + * represented as a u32 + * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and + * %NL80211_CMD_DISASSOCIATE, u16 + * + * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as + * a u32 + * + * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _before_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change + * due to considerations from a beacon hint. This attribute reflects + * the state of the channel _after_ the beacon hint processing. This + * attributes consists of a nested attribute containing + * NL80211_FREQUENCY_ATTR_* + * + * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported + * cipher suites + * + * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look + * for other networks on different channels + * + * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this + * is used, e.g., with %NL80211_CMD_AUTHENTICATE event + * + * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is + * used for the association (&enum nl80211_mfp, represented as a u32); + * this attribute can be used + * with %NL80211_CMD_ASSOCIATE request + * + * @NL80211_ATTR_STA_FLAGS2: Attribute containing a + * &struct nl80211_sta_flag_update. + * + * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls + * IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in + * station mode. If the flag is included in %NL80211_CMD_ASSOCIATE + * request, the driver will assume that the port is unauthorized until + * authorized by user space. Otherwise, port is marked authorized by + * default in station mode. + * + * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver. + * We recommend using nested, driver-specific attributes within this. + * + * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT + * event was due to the AP disconnecting the station, and not due to + * a local disconnect request. + * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT + * event (u16) + * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating + * that protected APs should be used. + * + * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to + * indicate which unicast key ciphers will be used with the connection + * (an array of u32). + * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate + * which group key cipher will be used with the connection (a u32). + * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate + * which WPA version(s) the AP we want to associate with is using + * (a u32 with flags from &enum nl80211_wpa_versions). + * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate + * which key management algorithm(s) to use (an array of u32). + * + * @NL80211_ATTR_REQ_IE: (Re)association request information elements as + * sent out by the card, for ROAM and successful CONNECT events. + * @NL80211_ATTR_RESP_IE: (Re)association response information elements as + * sent by peer, for ROAM and successful CONNECT events. + * + * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE + * commands to specify using a reassociate frame + * + * @NL80211_ATTR_KEY: key information in a nested attribute with + * %NL80211_KEY_* sub-attributes + * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect() + * and join_ibss(), key information is in a nested attribute each + * with %NL80211_KEY_* sub-attributes + * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * + * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for + * dumps. This number increases whenever the object list being + * dumped changes, and as such userspace can verify that it has + * obtained a complete and consistent snapshot by verifying that + * all dump messages contain the same generation number. If it + * changed then the list changed and the dump should be repeated + * completely from scratch. + * + * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface + * + * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of + * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute + * containing info as possible, see &enum survey_info. + * + * @NL80211_ATTR_MAX: highest attribute number currently defined + * @__NL80211_ATTR_AFTER_LAST: internal use + */ +enum nl80211_attrs { +/* don't change the order or add anything inbetween, this is ABI! */ + NL80211_ATTR_UNSPEC, + + NL80211_ATTR_WIPHY, + NL80211_ATTR_WIPHY_NAME, + + NL80211_ATTR_IFINDEX, + NL80211_ATTR_IFNAME, + NL80211_ATTR_IFTYPE, + + NL80211_ATTR_MAC, + + NL80211_ATTR_KEY_DATA, + NL80211_ATTR_KEY_IDX, + NL80211_ATTR_KEY_CIPHER, + NL80211_ATTR_KEY_SEQ, + NL80211_ATTR_KEY_DEFAULT, + + NL80211_ATTR_BEACON_INTERVAL, + NL80211_ATTR_DTIM_PERIOD, + NL80211_ATTR_BEACON_HEAD, + NL80211_ATTR_BEACON_TAIL, + + NL80211_ATTR_STA_AID, + NL80211_ATTR_STA_FLAGS, + NL80211_ATTR_STA_LISTEN_INTERVAL, + NL80211_ATTR_STA_SUPPORTED_RATES, + NL80211_ATTR_STA_VLAN, + NL80211_ATTR_STA_INFO, + + NL80211_ATTR_WIPHY_BANDS, + + NL80211_ATTR_MNTR_FLAGS, + + NL80211_ATTR_MESH_ID, + NL80211_ATTR_STA_PLINK_ACTION, + NL80211_ATTR_MPATH_NEXT_HOP, + NL80211_ATTR_MPATH_INFO, + + NL80211_ATTR_BSS_CTS_PROT, + NL80211_ATTR_BSS_SHORT_PREAMBLE, + NL80211_ATTR_BSS_SHORT_SLOT_TIME, + + NL80211_ATTR_HT_CAPABILITY, + + NL80211_ATTR_SUPPORTED_IFTYPES, + + NL80211_ATTR_REG_ALPHA2, + NL80211_ATTR_REG_RULES, + + NL80211_ATTR_MESH_PARAMS, + + NL80211_ATTR_BSS_BASIC_RATES, + + NL80211_ATTR_WIPHY_TXQ_PARAMS, + NL80211_ATTR_WIPHY_FREQ, + NL80211_ATTR_WIPHY_CHANNEL_TYPE, + + NL80211_ATTR_KEY_DEFAULT_MGMT, + + NL80211_ATTR_MGMT_SUBTYPE, + NL80211_ATTR_IE, + + NL80211_ATTR_MAX_NUM_SCAN_SSIDS, + + NL80211_ATTR_SCAN_FREQUENCIES, + NL80211_ATTR_SCAN_SSIDS, + NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */ + NL80211_ATTR_BSS, + + NL80211_ATTR_REG_INITIATOR, + NL80211_ATTR_REG_TYPE, + + NL80211_ATTR_SUPPORTED_COMMANDS, + + NL80211_ATTR_FRAME, + NL80211_ATTR_SSID, + NL80211_ATTR_AUTH_TYPE, + NL80211_ATTR_REASON_CODE, + + NL80211_ATTR_KEY_TYPE, + + NL80211_ATTR_MAX_SCAN_IE_LEN, + NL80211_ATTR_CIPHER_SUITES, + + NL80211_ATTR_FREQ_BEFORE, + NL80211_ATTR_FREQ_AFTER, + + NL80211_ATTR_FREQ_FIXED, + + + NL80211_ATTR_WIPHY_RETRY_SHORT, + NL80211_ATTR_WIPHY_RETRY_LONG, + NL80211_ATTR_WIPHY_FRAG_THRESHOLD, + NL80211_ATTR_WIPHY_RTS_THRESHOLD, + + NL80211_ATTR_TIMED_OUT, + + NL80211_ATTR_USE_MFP, + + NL80211_ATTR_STA_FLAGS2, + + NL80211_ATTR_CONTROL_PORT, + + NL80211_ATTR_TESTDATA, + + NL80211_ATTR_PRIVACY, + + NL80211_ATTR_DISCONNECTED_BY_AP, + NL80211_ATTR_STATUS_CODE, + + NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + NL80211_ATTR_CIPHER_SUITE_GROUP, + NL80211_ATTR_WPA_VERSIONS, + NL80211_ATTR_AKM_SUITES, + + NL80211_ATTR_REQ_IE, + NL80211_ATTR_RESP_IE, + + NL80211_ATTR_PREV_BSSID, + + NL80211_ATTR_KEY, + NL80211_ATTR_KEYS, + + NL80211_ATTR_PID, + + NL80211_ATTR_4ADDR, + + NL80211_ATTR_SURVEY_INFO, + + /* add attributes here, update the policy in nl80211.c */ + + __NL80211_ATTR_AFTER_LAST, + NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1 +}; + +/* source-level API compatibility */ +#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION + +/* + * Allow user space programs to use #ifdef on new attributes by defining them + * here + */ +#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT +#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY +#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES +#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS +#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ +#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE +#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE +#define NL80211_ATTR_IE NL80211_ATTR_IE +#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR +#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE +#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME +#define NL80211_ATTR_SSID NL80211_ATTR_SSID +#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE +#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE +#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE +#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP +#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS +#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES +#define NL80211_ATTR_KEY NL80211_ATTR_KEY +#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS + +#define NL80211_MAX_SUPP_RATES 32 +#define NL80211_MAX_SUPP_REG_RULES 32 +#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY 0 +#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY 16 +#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY 24 +#define NL80211_HT_CAPABILITY_LEN 26 + +#define NL80211_MAX_NR_CIPHER_SUITES 5 +#define NL80211_MAX_NR_AKM_SUITES 2 + +/** + * enum nl80211_iftype - (virtual) interface types + * + * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides + * @NL80211_IFTYPE_ADHOC: independent BSS member + * @NL80211_IFTYPE_STATION: managed BSS member + * @NL80211_IFTYPE_AP: access point + * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points + * @NL80211_IFTYPE_WDS: wireless distribution interface + * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames + * @NL80211_IFTYPE_MESH_POINT: mesh point + * @NL80211_IFTYPE_MAX: highest interface type number currently defined + * @__NL80211_IFTYPE_AFTER_LAST: internal use + * + * These values are used with the %NL80211_ATTR_IFTYPE + * to set the type of an interface. + * + */ +enum nl80211_iftype { + NL80211_IFTYPE_UNSPECIFIED, + NL80211_IFTYPE_ADHOC, + NL80211_IFTYPE_STATION, + NL80211_IFTYPE_AP, + NL80211_IFTYPE_AP_VLAN, + NL80211_IFTYPE_WDS, + NL80211_IFTYPE_MONITOR, + NL80211_IFTYPE_MESH_POINT, + + /* keep last */ + __NL80211_IFTYPE_AFTER_LAST, + NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_flags - station flags + * + * Station flags. When a station is added to an AP interface, it is + * assumed to be already associated (and hence authenticated.) + * + * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X) + * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames + * with short barker preamble + * @NL80211_STA_FLAG_WME: station is WME/QoS capable + * @NL80211_STA_FLAG_MFP: station uses management frame protection + */ +enum nl80211_sta_flags { + __NL80211_STA_FLAG_INVALID, + NL80211_STA_FLAG_AUTHORIZED, + NL80211_STA_FLAG_SHORT_PREAMBLE, + NL80211_STA_FLAG_WME, + NL80211_STA_FLAG_MFP, + + /* keep last */ + __NL80211_STA_FLAG_AFTER_LAST, + NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1 +}; + +/** + * struct nl80211_sta_flag_update - station flags mask/set + * @mask: mask of station flags to set + * @set: which values to set them to + * + * Both mask and set contain bits as per &enum nl80211_sta_flags. + */ +struct nl80211_sta_flag_update { + __u32 mask; + __u32 set; +} __attribute__((packed)); + +/** + * enum nl80211_rate_info - bitrate information + * + * These attribute types are used with %NL80211_STA_INFO_TXRATE + * when getting information about the bitrate of a station. + * + * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved + * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s) + * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8) + * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate + * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval + * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined + * @__NL80211_RATE_INFO_AFTER_LAST: internal use + */ +enum nl80211_rate_info { + __NL80211_RATE_INFO_INVALID, + NL80211_RATE_INFO_BITRATE, + NL80211_RATE_INFO_MCS, + NL80211_RATE_INFO_40_MHZ_WIDTH, + NL80211_RATE_INFO_SHORT_GI, + + /* keep last */ + __NL80211_RATE_INFO_AFTER_LAST, + NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_sta_info - station information + * + * These attribute types are used with %NL80211_ATTR_STA_INFO + * when getting information about a station. + * + * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved + * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs) + * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station) + * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station) + * @__NL80211_STA_INFO_AFTER_LAST: internal + * @NL80211_STA_INFO_MAX: highest possible station info attribute + * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm) + * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute + * containing info as possible, see &enum nl80211_sta_info_txrate. + * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station) + * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this + * station) + */ +enum nl80211_sta_info { + __NL80211_STA_INFO_INVALID, + NL80211_STA_INFO_INACTIVE_TIME, + NL80211_STA_INFO_RX_BYTES, + NL80211_STA_INFO_TX_BYTES, + NL80211_STA_INFO_LLID, + NL80211_STA_INFO_PLID, + NL80211_STA_INFO_PLINK_STATE, + NL80211_STA_INFO_SIGNAL, + NL80211_STA_INFO_TX_BITRATE, + NL80211_STA_INFO_RX_PACKETS, + NL80211_STA_INFO_TX_PACKETS, + + /* keep last */ + __NL80211_STA_INFO_AFTER_LAST, + NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mpath_flags - nl80211 mesh path flags + * + * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active + * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running + * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN + * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set + * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded + */ +enum nl80211_mpath_flags { + NL80211_MPATH_FLAG_ACTIVE = 1<<0, + NL80211_MPATH_FLAG_RESOLVING = 1<<1, + NL80211_MPATH_FLAG_SN_VALID = 1<<2, + NL80211_MPATH_FLAG_FIXED = 1<<3, + NL80211_MPATH_FLAG_RESOLVED = 1<<4, +}; + +/** + * enum nl80211_mpath_info - mesh path information + * + * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting + * information about a mesh path. + * + * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved + * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination + * @NL80211_ATTR_MPATH_SN: destination sequence number + * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path + * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now + * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in + * &enum nl80211_mpath_flags; + * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec + * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries + */ +enum nl80211_mpath_info { + __NL80211_MPATH_INFO_INVALID, + NL80211_MPATH_INFO_FRAME_QLEN, + NL80211_MPATH_INFO_SN, + NL80211_MPATH_INFO_METRIC, + NL80211_MPATH_INFO_EXPTIME, + NL80211_MPATH_INFO_FLAGS, + NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, + NL80211_MPATH_INFO_DISCOVERY_RETRIES, + + /* keep last */ + __NL80211_MPATH_INFO_AFTER_LAST, + NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band_attr - band attributes + * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved + * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band, + * an array of nested frequency attributes + * @NL80211_BAND_ATTR_RATES: supported bitrates in this band, + * an array of nested bitrate attributes + * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as + * defined in 802.11n + * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE + * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n + * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n + */ +enum nl80211_band_attr { + __NL80211_BAND_ATTR_INVALID, + NL80211_BAND_ATTR_FREQS, + NL80211_BAND_ATTR_RATES, + + NL80211_BAND_ATTR_HT_MCS_SET, + NL80211_BAND_ATTR_HT_CAPA, + NL80211_BAND_ATTR_HT_AMPDU_FACTOR, + NL80211_BAND_ATTR_HT_AMPDU_DENSITY, + + /* keep last */ + __NL80211_BAND_ATTR_AFTER_LAST, + NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA + +/** + * enum nl80211_frequency_attr - frequency attributes + * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz + * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current + * regulatory domain. + * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is + * permitted on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory + * on this channel in current regulatory domain. + * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm + * (100 * dBm). + */ +enum nl80211_frequency_attr { + __NL80211_FREQUENCY_ATTR_INVALID, + NL80211_FREQUENCY_ATTR_FREQ, + NL80211_FREQUENCY_ATTR_DISABLED, + NL80211_FREQUENCY_ATTR_PASSIVE_SCAN, + NL80211_FREQUENCY_ATTR_NO_IBSS, + NL80211_FREQUENCY_ATTR_RADAR, + NL80211_FREQUENCY_ATTR_MAX_TX_POWER, + + /* keep last */ + __NL80211_FREQUENCY_ATTR_AFTER_LAST, + NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1 +}; + +#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER + +/** + * enum nl80211_bitrate_attr - bitrate attributes + * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps + * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported + * in 2.4 GHz band. + */ +enum nl80211_bitrate_attr { + __NL80211_BITRATE_ATTR_INVALID, + NL80211_BITRATE_ATTR_RATE, + NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE, + + /* keep last */ + __NL80211_BITRATE_ATTR_AFTER_LAST, + NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_initiator - Indicates the initiator of a reg domain request + * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world + * regulatory domain. + * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the + * regulatory domain. + * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the + * wireless core it thinks its knows the regulatory domain we should be in. + * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an + * 802.11 country information element with regulatory information it + * thinks we should consider. + */ +enum nl80211_reg_initiator { + NL80211_REGDOM_SET_BY_CORE, + NL80211_REGDOM_SET_BY_USER, + NL80211_REGDOM_SET_BY_DRIVER, + NL80211_REGDOM_SET_BY_COUNTRY_IE, +}; + +/** + * enum nl80211_reg_type - specifies the type of regulatory domain + * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains + * to a specific country. When this is set you can count on the + * ISO / IEC 3166 alpha2 country code being valid. + * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory + * domain. + * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom + * driver specific world regulatory domain. These do not apply system-wide + * and are only applicable to the individual devices which have requested + * them to be applied. + * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product + * of an intersection between two regulatory domains -- the previously + * set regulatory domain on the system and the last accepted regulatory + * domain request to be processed. + */ +enum nl80211_reg_type { + NL80211_REGDOM_TYPE_COUNTRY, + NL80211_REGDOM_TYPE_WORLD, + NL80211_REGDOM_TYPE_CUSTOM_WORLD, + NL80211_REGDOM_TYPE_INTERSECTION, +}; + +/** + * enum nl80211_reg_rule_attr - regulatory rule attributes + * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional + * considerations for a given frequency range. These are the + * &enum nl80211_reg_rule_flags. + * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory + * rule in KHz. This is not a center of frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule + * in KHz. This is not a center a frequency but an actual regulatory + * band edge. + * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this + * frequency range, in KHz. + * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain + * for a given frequency range. The value is in mBi (100 * dBi). + * If you don't have one then don't send this. + * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for + * a given frequency range. The value is in mBm (100 * dBm). + */ +enum nl80211_reg_rule_attr { + __NL80211_REG_RULE_ATTR_INVALID, + NL80211_ATTR_REG_RULE_FLAGS, + + NL80211_ATTR_FREQ_RANGE_START, + NL80211_ATTR_FREQ_RANGE_END, + NL80211_ATTR_FREQ_RANGE_MAX_BW, + + NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN, + NL80211_ATTR_POWER_RULE_MAX_EIRP, + + /* keep last */ + __NL80211_REG_RULE_ATTR_AFTER_LAST, + NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_reg_rule_flags - regulatory rule flags + * + * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed + * @NL80211_RRF_NO_CCK: CCK modulation not allowed + * @NL80211_RRF_NO_INDOOR: indoor operation not allowed + * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed + * @NL80211_RRF_DFS: DFS support is required to be used + * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links + * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links + * @NL80211_RRF_PASSIVE_SCAN: passive scan is required + * @NL80211_RRF_NO_IBSS: no IBSS is allowed + */ +enum nl80211_reg_rule_flags { + NL80211_RRF_NO_OFDM = 1<<0, + NL80211_RRF_NO_CCK = 1<<1, + NL80211_RRF_NO_INDOOR = 1<<2, + NL80211_RRF_NO_OUTDOOR = 1<<3, + NL80211_RRF_DFS = 1<<4, + NL80211_RRF_PTP_ONLY = 1<<5, + NL80211_RRF_PTMP_ONLY = 1<<6, + NL80211_RRF_PASSIVE_SCAN = 1<<7, + NL80211_RRF_NO_IBSS = 1<<8, +}; + +/** + * enum nl80211_survey_info - survey information + * + * These attribute types are used with %NL80211_ATTR_SURVEY_INFO + * when getting information about a survey. + * + * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved + * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel + * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm) + */ +enum nl80211_survey_info { + __NL80211_SURVEY_INFO_INVALID, + NL80211_SURVEY_INFO_FREQUENCY, + NL80211_SURVEY_INFO_NOISE, + + /* keep last */ + __NL80211_SURVEY_INFO_AFTER_LAST, + NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1 +}; + +/** + * enum nl80211_mntr_flags - monitor configuration flags + * + * Monitor configuration flags. + * + * @__NL80211_MNTR_FLAG_INVALID: reserved + * + * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS + * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP + * @NL80211_MNTR_FLAG_CONTROL: pass control frames + * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering + * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing. + * overrides all other flags. + * + * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use + * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag + */ +enum nl80211_mntr_flags { + __NL80211_MNTR_FLAG_INVALID, + NL80211_MNTR_FLAG_FCSFAIL, + NL80211_MNTR_FLAG_PLCPFAIL, + NL80211_MNTR_FLAG_CONTROL, + NL80211_MNTR_FLAG_OTHER_BSS, + NL80211_MNTR_FLAG_COOK_FRAMES, + + /* keep last */ + __NL80211_MNTR_FLAG_AFTER_LAST, + NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1 +}; + +/** + * enum nl80211_meshconf_params - mesh configuration parameters + * + * Mesh configuration parameters + * + * @__NL80211_MESHCONF_INVALID: internal use + * + * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in + * millisecond units, used by the Peer Link Open message + * + * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in + * millisecond units, used by the peer link management to close a peer link + * + * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in + * millisecond units + * + * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed + * on this mesh interface + * + * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link + * open retries that can be sent to establish a new peer link instance in a + * mesh + * + * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh + * point. + * + * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically + * open peer links when we detect compatible mesh peers. + * + * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames + * containing a PREQ that an MP can send to a particular destination (path + * target) + * + * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths + * (in milliseconds) + * + * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait + * until giving up on a path discovery (in milliseconds) + * + * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh + * points receiving a PREQ shall consider the forwarding information from the + * root to be valid. (TU = time unit) + * + * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in + * TUs) during which an MP can send only one action frame containing a PREQ + * reference element + * + * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs) + * that it takes for an HWMP information element to propagate across the mesh + * + * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not + * + * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute + * + * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use + */ +enum nl80211_meshconf_params { + __NL80211_MESHCONF_INVALID, + NL80211_MESHCONF_RETRY_TIMEOUT, + NL80211_MESHCONF_CONFIRM_TIMEOUT, + NL80211_MESHCONF_HOLDING_TIMEOUT, + NL80211_MESHCONF_MAX_PEER_LINKS, + NL80211_MESHCONF_MAX_RETRIES, + NL80211_MESHCONF_TTL, + NL80211_MESHCONF_AUTO_OPEN_PLINKS, + NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, + NL80211_MESHCONF_PATH_REFRESH_TIME, + NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, + NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, + NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, + NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, + NL80211_MESHCONF_HWMP_ROOTMODE, + + /* keep last */ + __NL80211_MESHCONF_ATTR_AFTER_LAST, + NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1 +}; + +/** + * enum nl80211_txq_attr - TX queue parameter attributes + * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved + * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*) + * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning + * disabled + * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form + * 2^n-1 in the range 1..32767] + * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255] + * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal + * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number + */ +enum nl80211_txq_attr { + __NL80211_TXQ_ATTR_INVALID, + NL80211_TXQ_ATTR_QUEUE, + NL80211_TXQ_ATTR_TXOP, + NL80211_TXQ_ATTR_CWMIN, + NL80211_TXQ_ATTR_CWMAX, + NL80211_TXQ_ATTR_AIFS, + + /* keep last */ + __NL80211_TXQ_ATTR_AFTER_LAST, + NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1 +}; + +enum nl80211_txq_q { + NL80211_TXQ_Q_VO, + NL80211_TXQ_Q_VI, + NL80211_TXQ_Q_BE, + NL80211_TXQ_Q_BK +}; + +enum nl80211_channel_type { + NL80211_CHAN_NO_HT, + NL80211_CHAN_HT20, + NL80211_CHAN_HT40MINUS, + NL80211_CHAN_HT40PLUS +}; + +/** + * enum nl80211_bss - netlink attributes for a BSS + * + * @__NL80211_BSS_INVALID: invalid + * @NL80211_BSS_FREQUENCY: frequency in MHz (u32) + * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64) + * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) + * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) + * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the + * raw information elements from the probe response/beacon (bin) + * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon + * in mBm (100 * dBm) (s32) + * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon + * in unspecified units, scaled to 0..100 (u8) + * @NL80211_BSS_STATUS: status, if this BSS is "used" + * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @__NL80211_BSS_AFTER_LAST: internal + * @NL80211_BSS_MAX: highest BSS attribute + */ +enum nl80211_bss { + __NL80211_BSS_INVALID, + NL80211_BSS_BSSID, + NL80211_BSS_FREQUENCY, + NL80211_BSS_TSF, + NL80211_BSS_BEACON_INTERVAL, + NL80211_BSS_CAPABILITY, + NL80211_BSS_INFORMATION_ELEMENTS, + NL80211_BSS_SIGNAL_MBM, + NL80211_BSS_SIGNAL_UNSPEC, + NL80211_BSS_STATUS, + NL80211_BSS_SEEN_MS_AGO, + + /* keep last */ + __NL80211_BSS_AFTER_LAST, + NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1 +}; + +/** + * enum nl80211_bss_status - BSS "status" + */ +enum nl80211_bss_status { + NL80211_BSS_STATUS_AUTHENTICATED, + NL80211_BSS_STATUS_ASSOCIATED, + NL80211_BSS_STATUS_IBSS_JOINED, +}; + +/** + * enum nl80211_auth_type - AuthenticationType + * + * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication + * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only) + * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r) + * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP) + * @__NL80211_AUTHTYPE_NUM: internal + * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm + * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by + * trying multiple times); this is invalid in netlink -- leave out + * the attribute for this on CONNECT commands. + */ +enum nl80211_auth_type { + NL80211_AUTHTYPE_OPEN_SYSTEM, + NL80211_AUTHTYPE_SHARED_KEY, + NL80211_AUTHTYPE_FT, + NL80211_AUTHTYPE_NETWORK_EAP, + + /* keep last */ + __NL80211_AUTHTYPE_NUM, + NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1, + NL80211_AUTHTYPE_AUTOMATIC +}; + +/** + * enum nl80211_key_type - Key Type + * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key + * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key + * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS) + */ +enum nl80211_key_type { + NL80211_KEYTYPE_GROUP, + NL80211_KEYTYPE_PAIRWISE, + NL80211_KEYTYPE_PEERKEY, +}; + +/** + * enum nl80211_mfp - Management frame protection state + * @NL80211_MFP_NO: Management frame protection not used + * @NL80211_MFP_REQUIRED: Management frame protection required + */ +enum nl80211_mfp { + NL80211_MFP_NO, + NL80211_MFP_REQUIRED, +}; + +enum nl80211_wpa_versions { + NL80211_WPA_VERSION_1 = 1 << 0, + NL80211_WPA_VERSION_2 = 1 << 1, +}; + +/** + * enum nl80211_key_attributes - key attributes + * @__NL80211_KEY_INVALID: invalid + * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of + * 16 bytes encryption key followed by 8 bytes each for TX and RX MIC + * keys + * @NL80211_KEY_IDX: key ID (u8, 0-3) + * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11 + * section 7.3.2.25.1, e.g. 0x000FAC04) + * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and + * CCMP keys, each six bytes in little endian + * @NL80211_KEY_DEFAULT: flag indicating default key + * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key + * @__NL80211_KEY_AFTER_LAST: internal + * @NL80211_KEY_MAX: highest key attribute + */ +enum nl80211_key_attributes { + __NL80211_KEY_INVALID, + NL80211_KEY_DATA, + NL80211_KEY_IDX, + NL80211_KEY_CIPHER, + NL80211_KEY_SEQ, + NL80211_KEY_DEFAULT, + NL80211_KEY_DEFAULT_MGMT, + + /* keep last */ + __NL80211_KEY_AFTER_LAST, + NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 +}; + +#endif /* __LINUX_NL80211_H */ diff --git a/nvs.c b/nvs.c new file mode 100644 index 0000000..2e9e146 --- /dev/null +++ b/nvs.c @@ -0,0 +1,1283 @@ +/* + * PLT utility for wireless chip supported by TI's driver wl12xx + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "nl80211.h" + +#include "calibrator.h" +#include "plt.h" +#include "ini.h" +/* 2048 - it should be enough for any chip, until... 22dec2010 */ +#define BUF_SIZE_4_NVS_FILE 2048 + +static const char if_name_fmt[] = "wlan%d"; + +static char* get_opt_file(int argc, char **argv, char *dir, char *def) +{ + char *name = NULL; + if (argc < 0) + return NULL; + else if (argc == 0) { + name = def; + fprintf(stderr, "\nThe path to input %s file not provided, " + "use default (%s)\n", dir, name); + } + else + name = *argv; + return name; +} + + +char *get_opt_nvsinfile(int argc, char **argv) +{ + char *name = get_opt_file(argc, argv, "input", CURRENT_NVS_NAME); + if (file_exist(name) < 0) { + fprintf(stderr, "File not found %s\n", name); + return NULL; + } + return name; +} + +char *get_opt_nvsoutfile(int argc, char **argv) +{ + char *name = get_opt_file(argc, argv, "output", NEW_NVS_NAME); + return name; +} + +int nvs_set_mac(char *nvsfile, char *mac) +{ + unsigned char mac_buff[12]; + unsigned int in_mac[6]; + int fd; + unsigned int lower; + + if (mac) { + int ret = + sscanf(mac, "%2x:%2x:%2x:%2x:%2x:%2x", + &in_mac[0], &in_mac[1], &in_mac[2], + &in_mac[3], &in_mac[4], &in_mac[5]); + if (ret != 6) { + fprintf(stderr, "MAC address is not valid: %s\n", mac); + return -1; + } + } + else { + fprintf(stderr, "No MAC address specified\n"); + return -1; + } + + fd = open(nvsfile, O_RDWR); + if (fd < 0) { + perror("Error opening file for reading"); + return 1; + } + + read(fd, mac_buff, 12); + mac_buff[11] = (unsigned char)in_mac[0]; + mac_buff[10] = (unsigned char)in_mac[1]; + mac_buff[6] = (unsigned char)in_mac[2]; + mac_buff[5] = (unsigned char)in_mac[3]; + mac_buff[4] = (unsigned char)in_mac[4]; + mac_buff[3] = (unsigned char)in_mac[5]; + + lseek(fd, 0L, 0); + + /* we need at least two valid NIC addresses */ + lower = (in_mac[3] << 16) + (in_mac[4] << 8) + in_mac[5]; + if (lower + 1 > 0xffffff) + fprintf(stderr, + "WARNING: NIC part of the MAC address wraps around!\n"); + + printf("Writing mac address %s to file %s\n", mac, nvsfile); + write(fd, mac_buff, 12); + + close(fd); + return 0; +} + +int nvs_fill_radio_params(int fd, struct wl12xx_ini *ini, char *buf) +{ + struct wl1271_nvs_ini gp; + int fem_idx; + + if (ini) { + /* Reset local NVS Radio Params */ + memset(&gp, 0, sizeof(gp)); + + /* Copy from INI to NVS */ + gp.general_params = ini->ini1271.general_params; + gp.stat_radio_params_2 = ini->ini1271.stat_radio_params_2; + + if (gp.general_params.dual_mode_select) + gp.stat_radio_params_5 = ini->ini1271.stat_radio_params_5; + + if (gp.general_params.tx_bip_fem_auto_detect) { + /* For backward compatibility we fill the first 2 FEM entries */ + gp.dyn_radio_params_2[0].params = + ini->ini1271.dyn_radio_params_2[0].params; + gp.dyn_radio_params_2[1].params = + ini->ini1271.dyn_radio_params_2[1].params; + + if (gp.general_params.dual_mode_select) { + gp.dyn_radio_params_5[0].params = + ini->ini1271.dyn_radio_params_5[0].params; + gp.dyn_radio_params_5[1].params = + ini->ini1271.dyn_radio_params_5[1].params; + } + } + else { + /* + * Translate to NVS FEM entry + * In case of fem manufacturer 1 (TQS) use FEM idx 1 to maintain + * backward compatibilty. For all other types use idx 0 + */ + fem_idx = WL12XX_FEM_TO_NVS_ENTRY( + gp.general_params.tx_bip_fem_manufacturer); + + gp.dyn_radio_params_2[fem_idx].params = + ini->ini1271.dyn_radio_params_2 + [gp.general_params.tx_bip_fem_manufacturer].params; + + if (gp.general_params.dual_mode_select) + gp.dyn_radio_params_5[fem_idx].params = + ini->ini1271.dyn_radio_params_5 + [gp.general_params.tx_bip_fem_manufacturer].params; + } + + write(fd, (const void *)&gp, sizeof(gp)); + } else { + char *p = buf + 0x1D4; + write(fd, (const void *)p, sizeof(gp)); + } + + return 0; +} + +static int nvs_fill_radio_params_128x(int fd, struct wl12xx_ini *ini, char *buf) +{ + struct wl128x_nvs_ini gp; + int fem_idx; + + if (ini) { + /* Reset local NVS Radio Params */ + memset(&gp, 0, sizeof(gp)); + + /* Copy from INI to NVS */ + gp.general_params = ini->ini128x.general_params; + gp.fem_vendor_and_options = ini->ini128x.fem_vendor_and_options; + gp.stat_radio_params_2 = ini->ini128x.stat_radio_params_2; + + if (gp.general_params.dual_mode_select) + gp.stat_radio_params_5 = ini->ini128x.stat_radio_params_5; + + if (gp.general_params.tx_bip_fem_auto_detect) { + /* For backward compatibility we fill the first 2 FEM entries */ + gp.dyn_radio_params_2[0].params = + ini->ini128x.dyn_radio_params_2[0].params; + gp.dyn_radio_params_2[1].params = + ini->ini128x.dyn_radio_params_2[1].params; + + if (gp.general_params.dual_mode_select) { + gp.dyn_radio_params_5[0].params = + ini->ini128x.dyn_radio_params_5[0].params; + gp.dyn_radio_params_5[1].params = + ini->ini128x.dyn_radio_params_5[1].params; + } + } + else { + /* + * Translate to NVS FEM entry + * In case of fem manufacturer 1 (TQS) use FEM idx 1 to maintain + * backward compatibilty. For all other types use idx 0 + */ + fem_idx = WL12XX_FEM_TO_NVS_ENTRY( + gp.general_params.tx_bip_fem_manufacturer); + + gp.dyn_radio_params_2[fem_idx].params = + ini->ini128x.dyn_radio_params_2 + [gp.general_params.tx_bip_fem_manufacturer].params; + + if (gp.general_params.dual_mode_select) + gp.dyn_radio_params_5[fem_idx].params = + ini->ini128x.dyn_radio_params_5 + [gp.general_params.tx_bip_fem_manufacturer].params; + } + + write(fd, (const void *)&gp, sizeof(gp)); + } else { + char *p = buf + 0x1D4; + write(fd, (const void *)p, sizeof(gp)); + } + + return 0; +} + +int nvs_set_autofem(int fd, char *buf, unsigned char val) +{ + size_t size, i; + struct wl1271_ini *gp; + unsigned char *c; + + if (buf == NULL) + return 1; + + gp = (struct wl1271_ini *)(buf+0x1d4); + gp->general_params.tx_bip_fem_auto_detect = val; + + size = sizeof(struct wl1271_ini); + + c = (unsigned char *)gp; + + for (i = 0; i < size; i++) + write(fd, c++, 1); + + return 0; +} + +int nvs_set_autofem_128x(int fd, char *buf, unsigned char val) +{ + size_t size, i; + struct wl128x_ini *gp; + unsigned char *c; + + if (buf == NULL) + return 1; + + gp = (struct wl128x_ini *)(buf+0x1d4); + gp->general_params.tx_bip_fem_auto_detect = val; + + size = sizeof(struct wl128x_ini); + + c = (unsigned char *)gp; + + for (i = 0; i < size; i++) + write(fd, c++, 1); + + return 0; +} + +int nvs_set_fem_manuf(int fd, char *buf, unsigned char val) +{ + size_t size, i; + struct wl1271_ini *gp; + unsigned char *c; + + if (buf == NULL) + return 1; + + gp = (struct wl1271_ini *)(buf+0x1d4); + gp->general_params.tx_bip_fem_manufacturer = val; + + size = sizeof(struct wl1271_ini); + + c = (unsigned char *)gp; + + for (i = 0; i < size; i++) + write(fd, c++, 1); + + return 0; +} + +int nvs_set_fem_manuf_128x(int fd, char *buf, unsigned char val) +{ + size_t size, i; + struct wl128x_ini *gp; + unsigned char *c; + + if (buf == NULL) + return 1; + + gp = (struct wl128x_ini *)(buf+0x1d4); + gp->general_params.tx_bip_fem_manufacturer = val; + + size = sizeof(struct wl128x_ini); + + c = (unsigned char *)gp; + + for (i = 0; i < size; i++) + write(fd, c++, 1); + + return 0; +} + +static struct wl12xx_nvs_ops wl1271_nvs_ops = { + .nvs_fill_radio_prms = nvs_fill_radio_params, + .nvs_set_autofem = nvs_set_autofem, + .nvs_set_fem_manuf = nvs_set_fem_manuf, +}; + +static struct wl12xx_nvs_ops wl128x_nvs_ops = { + .nvs_fill_radio_prms = nvs_fill_radio_params_128x, + .nvs_set_autofem = nvs_set_autofem_128x, + .nvs_set_fem_manuf = nvs_set_fem_manuf_128x, +}; + +int get_mac_addr(int ifc_num, unsigned char *mac_addr) +{ + int s; + struct ifreq ifr; +#if 0 + if (ifc_num < 0 || ifc_num >= ETH_DEV_MAX) + return 1; +#endif + s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (s < 0) { + fprintf(stderr, "unable to socket (%s)\n", strerror(errno)); + return 1; + } + + memset(&ifr, 0, sizeof(struct ifreq)); + sprintf(ifr.ifr_name, if_name_fmt, ifc_num) ; + if (ioctl(s, SIOCGIFHWADDR, &ifr) < 0) { + fprintf(stderr, "unable to ioctl (%s)\n", strerror(errno)); + close(s); + return 1; + } + + close(s); + + memcpy(mac_addr, &ifr.ifr_ifru.ifru_hwaddr.sa_data[0], 6); + + return 0; +} + +int file_exist(const char *filename) +{ + struct stat buf; + int ret; + + if (filename == NULL) { + fprintf(stderr, "wrong parameter\n"); + return -1; + } + + ret = stat(filename, &buf); + if (ret != 0) { + return -1; + } + + return (int)buf.st_size; +} + +void cfg_nvs_ops(struct wl12xx_common *cmn) +{ + if (cmn->arch == WL1271_ARCH) + cmn->nvs_ops = &wl1271_nvs_ops; + else + cmn->nvs_ops = &wl128x_nvs_ops; +} + +static int read_from_current_nvs(const char *nvs_file, + char *buf, int size, int *nvs_sz) +{ + int curr_nvs, ret; + + curr_nvs = open(nvs_file, O_RDONLY, S_IRUSR | S_IWUSR); + if (curr_nvs < 0) { + fprintf(stderr, "%s> Unable to open NVS file for reference " + "(%s)\n", __func__, strerror(errno)); + return 1; + } + + ret = read(curr_nvs, buf, size); + if (ret < 0) { + fprintf(stderr, "Fail to read file %s (%s)\n", nvs_file, + strerror(errno)); + close(curr_nvs); + return 1; + } + + if (nvs_sz) + *nvs_sz = ret; + + close(curr_nvs); + + //printf("Read NVS file (%s) of size %d\n", nvs_file, ret); + + return 0; +} + +static int read_nvs(const char *nvs_file, char *buf, + int size, int *nvs_sz) +{ + int fl_sz; + fl_sz = file_exist(nvs_file); + if (fl_sz < 0) { + fprintf(stderr, "File %s not exists\n", nvs_file); + return 1; + } + + return read_from_current_nvs(nvs_file, buf, size, nvs_sz); +} + +static int fill_nvs_def_rx_params(int fd) +{ + unsigned char type = eNVS_RADIO_RX_PARAMETERS; + unsigned short length = NVS_RX_PARAM_LENGTH; + int i; + + /* Rx type */ + write(fd, &type, 1); + + /* Rx length */ + write(fd, &length, 2); + + type = DEFAULT_EFUSE_VALUE; /* just reuse of var */ + for (i = 0; i < NVS_RX_PARAM_LENGTH; i++) + write(fd, &type, 1); + + return 0; +} + +static void nvs_parse_data(const unsigned char *buf, + struct wl1271_cmd_cal_p2g *pdata, unsigned int *pver) +{ +#define BUFFER_INDEX (buf_idx + START_PARAM_INDEX + info_idx) + unsigned short buf_idx; + unsigned char tlv_type; + unsigned short tlv_len; + unsigned short info_idx; + unsigned int nvsTypeInfo = 0; + unsigned char nvs_ver_oct_idx; + unsigned char shift; + + for (buf_idx = 0; buf_idx < NVS_TOTAL_LENGTH;) { + tlv_type = buf[buf_idx]; + + /* fill the correct mode to fill the NVS struct buffer */ + /* if the tlv_type is the last type break from the loop */ + switch (tlv_type) { + case eNVS_RADIO_TX_PARAMETERS: + nvsTypeInfo = eNVS_RADIO_TX_TYPE_PARAMETERS_INFO; + break; + case eNVS_RADIO_RX_PARAMETERS: + nvsTypeInfo = eNVS_RADIO_RX_TYPE_PARAMETERS_INFO; + break; + case eNVS_VERSION: + for (*pver = 0, nvs_ver_oct_idx = 0; + nvs_ver_oct_idx < NVS_VERSION_PARAMETER_LENGTH; + nvs_ver_oct_idx++) { + shift = 8 * (NVS_VERSION_PARAMETER_LENGTH - + 1 - nvs_ver_oct_idx); + *pver += ((buf[buf_idx + START_PARAM_INDEX + + nvs_ver_oct_idx]) << shift); + } + break; + case eTLV_LAST: + default: + return; + } + + tlv_len = (buf[buf_idx + START_LENGTH_INDEX + 1] << 8) + + buf[buf_idx + START_LENGTH_INDEX]; + + /* if TLV type is not NVS ver fill the NVS according */ + /* to the mode TX/RX */ + if ((eNVS_RADIO_TX_PARAMETERS == tlv_type) || + (eNVS_RADIO_RX_PARAMETERS == tlv_type)) { + pdata[nvsTypeInfo].type = tlv_type; + pdata[nvsTypeInfo].len = tlv_len; + + for (info_idx = 0; (info_idx < tlv_len) && + (BUFFER_INDEX < NVS_TOTAL_LENGTH); + info_idx++) { + pdata[nvsTypeInfo].buf[info_idx] = + buf[BUFFER_INDEX]; + } + } + + /* increment to the next TLV */ + buf_idx += START_PARAM_INDEX + tlv_len; + } +} + +static int nvs_fill_version(int fd, unsigned int *pdata) +{ + unsigned char tmp = eNVS_VERSION; + unsigned short tmp2 = NVS_VERSION_PARAMETER_LENGTH; + + write(fd, &tmp, 1); + + write(fd, &tmp2, 2); + + tmp = (*pdata >> 16) & 0xff; + write(fd, &tmp, 1); + + tmp = (*pdata >> 8) & 0xff; + write(fd, &tmp, 1); + + tmp = *pdata & 0xff; + write(fd, &tmp, 1); + + return 0; +} + +static int nvs_fill_old_rx_data(int fd, const unsigned char *buf, + unsigned short len) +{ + unsigned short idx; + unsigned char rx_type; + + /* RX BiP type */ + rx_type = eNVS_RADIO_RX_PARAMETERS; + write(fd, &rx_type, 1); + + /* RX BIP Length */ + write(fd, &len, 2); + + for (idx = 0; idx < len; idx++) + write(fd, &(buf[idx]), 1); + + return 0; +} + +static int nvs_upd_nvs_part(int fd, char *buf) +{ + char *p = buf; + + write(fd, p, 0x1D4); + + return 0; +} + +static int nvs_fill_nvs_part(int fd) +{ + int i; + unsigned char mac_addr[MAC_ADDR_LEN] = { + 0x0b, 0xad, 0xde, 0xad, 0xbe, 0xef + }; + __le16 nvs_tx_sz = NVS_TX_PARAM_LENGTH; + __le32 nvs_ver = 0x0; + const unsigned char vals[] = { + 0x0, 0x1, 0x6d, 0x54, 0x71, eTLV_LAST, eNVS_RADIO_TX_PARAMETERS + }; + + write(fd, &vals[1], 1); + write(fd, &vals[2], 1); + write(fd, &vals[3], 1); +#if 0 + if (get_mac_addr(0, mac_addr)) { + fprintf(stderr, "%s> Fail to get mac address\n", __func__); + return 1; + } +#endif + /* write down MAC address in new NVS file */ + write(fd, &mac_addr[5], 1); + write(fd, &mac_addr[4], 1); + write(fd, &mac_addr[3], 1); + write(fd, &mac_addr[2], 1); + + write(fd, &vals[1], 1); + write(fd, &vals[4], 1); + write(fd, &vals[3], 1); + + write(fd, &mac_addr[1], 1); + write(fd, &mac_addr[0], 1); + + write(fd, &vals[0], 1); + write(fd, &vals[0], 1); + + /* fill end burst transaction zeros */ + for (i = 0; i < NVS_END_BURST_TRANSACTION_LENGTH; i++) + write(fd, &vals[0], 1); + + /* fill zeros to Align TLV start address */ + for (i = 0; i < NVS_ALING_TLV_START_ADDRESS_LENGTH; i++) + write(fd, &vals[0], 1); + + /* Fill Tx calibration part */ + write(fd, &vals[6], 1); + write(fd, &nvs_tx_sz, 2); + + for (i = 0; i < nvs_tx_sz; i++) + write(fd, &vals[0], 1); + + /* Fill Rx calibration part */ + fill_nvs_def_rx_params(fd); + + /* fill NVS version */ + if (nvs_fill_version(fd, &nvs_ver)) + fprintf(stderr, "Fail to fill version\n"); + + /* fill end of NVS */ + write(fd, &vals[5], 1); /* eTLV_LAST */ + write(fd, &vals[5], 1); /* eTLV_LAST */ + write(fd, &vals[0], 1); + write(fd, &vals[0], 1); + + return 0; +} + +int prepare_nvs_file(void *arg, char *file_name) +{ + int new_nvs, i, nvs_size; + unsigned char mac_addr[MAC_ADDR_LEN]; + struct wl1271_cmd_cal_p2g *pdata; + struct wl1271_cmd_cal_p2g old_data[eNUMBER_RADIO_TYPE_PARAMETERS_INFO]; + char buf[2048]; + unsigned char *p; + struct wl12xx_common cmn = { + .arch = UNKNOWN_ARCH, + .parse_ops = NULL + }; + + const unsigned char vals[] = { + 0x0, 0x1, 0x6d, 0x54, 0x71, eTLV_LAST, eNVS_RADIO_TX_PARAMETERS + }; + + if (arg == NULL) { + fprintf(stderr, "%s> Missing args\n", __func__); + return 1; + } + + if (read_nvs(file_name, buf, BUF_SIZE_4_NVS_FILE, &nvs_size)) + return 1; + + switch (nvs_size) { + case NVS_FILE_SIZE_127X: + cmn.arch = WL1271_ARCH; + break; + case NVS_FILE_SIZE_128X: + cmn.arch = WL128X_ARCH; + break; + default: + fprintf(stderr, "%s> Wrong file size\n", __func__); + return 1; + } + + cfg_nvs_ops(&cmn); + + /* create new NVS file */ + new_nvs = open(file_name, + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (new_nvs < 0) { + fprintf(stderr, "%s> Unable to open new NVS file\n", __func__); + return 1; + } + + write(new_nvs, &vals[1], 1); + write(new_nvs, &vals[2], 1); + write(new_nvs, &vals[3], 1); + + if (get_mac_addr(0, mac_addr)) { + fprintf(stderr, "%s> Fail to get mac addr\n", __func__); + close(new_nvs); + return 1; + } + + /* write down MAC address in new NVS file */ + write(new_nvs, &mac_addr[5], 1); + write(new_nvs, &mac_addr[4], 1); + write(new_nvs, &mac_addr[3], 1); + write(new_nvs, &mac_addr[2], 1); + + write(new_nvs, &vals[1], 1); + write(new_nvs, &vals[4], 1); + write(new_nvs, &vals[3], 1); + + write(new_nvs, &mac_addr[1], 1); + write(new_nvs, &mac_addr[0], 1); + + write(new_nvs, &vals[0], 1); + write(new_nvs, &vals[0], 1); + + /* fill end burst transaction zeros */ + for (i = 0; i < NVS_END_BURST_TRANSACTION_LENGTH; i++) + write(new_nvs, &vals[0], 1); + + /* fill zeros to Align TLV start address */ + for (i = 0; i < NVS_ALING_TLV_START_ADDRESS_LENGTH; i++) + write(new_nvs, &vals[0], 1); + + /* Fill TxBip */ + pdata = (struct wl1271_cmd_cal_p2g *)arg; + + write(new_nvs, &vals[6], 1); + write(new_nvs, &pdata->len, 2); + + p = (unsigned char *)&(pdata->buf); + for (i = 0; i < pdata->len; i++) + write(new_nvs, p++, 1); + + { + unsigned int old_ver; +#if 0 + { + unsigned char *p = (unsigned char *)buf; + for (old_ver = 0; old_ver < 1024; old_ver++) { + if (old_ver%16 == 0) + printf("\n"); + printf("%02x ", *p++); + } + } +#endif + memset(old_data, 0, + sizeof(struct wl1271_cmd_cal_p2g)* + eNUMBER_RADIO_TYPE_PARAMETERS_INFO); + nvs_parse_data((const unsigned char *)&buf[NVS_PRE_PARAMETERS_LENGTH], + old_data, &old_ver); + + nvs_fill_old_rx_data(new_nvs, + old_data[eNVS_RADIO_RX_TYPE_PARAMETERS_INFO].buf, + old_data[eNVS_RADIO_RX_TYPE_PARAMETERS_INFO].len); + } + + /* fill NVS version */ + if (nvs_fill_version(new_nvs, &pdata->ver)) + fprintf(stderr, "Fail to fill version\n"); + + /* fill end of NVS */ + write(new_nvs, &vals[5], 1); /* eTLV_LAST */ + write(new_nvs, &vals[5], 1); /* eTLV_LAST */ + write(new_nvs, &vals[0], 1); + write(new_nvs, &vals[0], 1); + + /* fill radio params */ + if (cmn.nvs_ops->nvs_fill_radio_prms(new_nvs, NULL, buf)) + fprintf(stderr, "Fail to fill radio params\n"); + + close(new_nvs); + + return 0; +} + +int create_nvs_file(struct wl12xx_common *cmn) +{ + int new_nvs, res = 0; + char buf[2048]; + + /* create new NVS file */ + new_nvs = open(cmn->nvs_name, + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (new_nvs < 0) { + fprintf(stderr, "%s> Unable to open new NVS file\n", __func__); + return 1; + } + + /* fill nvs part */ + if (nvs_fill_nvs_part(new_nvs)) { + fprintf(stderr, "Fail to fill NVS part\n"); + res = 1; + + goto out; + } + + /* fill radio params */ + if (cmn->nvs_ops->nvs_fill_radio_prms(new_nvs, &cmn->ini, buf)) { + fprintf(stderr, "Fail to fill radio params\n"); + res = 1; + } + +out: + close(new_nvs); + + return res; +} + +int update_nvs_file(const char *nvs_infile, const char *nvs_outfile, struct wl12xx_common *cmn) +{ + int new_nvs, res = 0; + char buf[2048]; + + res = read_nvs(nvs_infile, buf, BUF_SIZE_4_NVS_FILE, NULL); + if (res) + return 1; + + /* create new NVS file */ + new_nvs = open(nvs_outfile, + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (new_nvs < 0) { + fprintf(stderr, "%s> Unable to open new NVS file\n", __func__); + return 1; + } + + /* fill nvs part */ + if (nvs_upd_nvs_part(new_nvs, buf)) { + fprintf(stderr, "Fail to fill NVS part\n"); + res = 1; + + goto out; + } + + /* fill radio params */ + if (cmn->nvs_ops->nvs_fill_radio_prms(new_nvs, &cmn->ini, buf)) { + printf("Fail to fill radio params\n"); + res = 1; + } + +out: + close(new_nvs); + + return res; +} + +int dump_nvs_file(const char *nvs_file) +{ + int sz=0, size; + char buf[2048]; + unsigned char *p = (unsigned char *)buf; + + if (read_nvs(nvs_file, buf, BUF_SIZE_4_NVS_FILE, &size)) + return 1; + + printf("\nThe size is %d bytes\n", size); + + for ( ; sz < size; sz++) { + if (sz%16 == 0) + printf("\n %04X ", sz); + printf("%02x ", *p++); + } + printf("\n"); + + return 0; +} + +int set_nvs_file_autofem(const char *nvs_file, unsigned char val, + struct wl12xx_common *cmn) +{ + int new_nvs, res = 0; + char buf[2048]; + int nvs_file_sz; + + res = read_nvs(nvs_file, buf, BUF_SIZE_4_NVS_FILE, &nvs_file_sz); + if (res) + return 1; + + if (nvs_get_arch(nvs_file_sz, cmn)) { + fprintf(stderr, "Fail to define architecture\n"); + return 1; + } + + cfg_nvs_ops(cmn); + + /* create new NVS file */ + new_nvs = open(nvs_file, + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (new_nvs < 0) { + fprintf(stderr, "%s> Unable to open new NVS file\n", __func__); + return 1; + } + + /* fill nvs part */ + if (nvs_upd_nvs_part(new_nvs, buf)) { + fprintf(stderr, "Fail to fill NVS part\n"); + res = 1; + + goto out; + } + + /* fill radio params */ + if (cmn->nvs_ops->nvs_set_autofem(new_nvs, buf, val)) { + printf("Fail to fill radio params\n"); + res = 1; + } + +out: + close(new_nvs); + + return res; +} + +int set_nvs_file_fem_manuf(const char *nvs_file, unsigned char val, + struct wl12xx_common *cmn) +{ + int new_nvs, res = 0; + char buf[2048]; + int nvs_file_sz; + + res = read_nvs(nvs_file, buf, BUF_SIZE_4_NVS_FILE, &nvs_file_sz); + if (res) + return 1; + + if (nvs_get_arch(nvs_file_sz, cmn)) { + fprintf(stderr, "Fail to define architecture\n"); + return 1; + } + + cfg_nvs_ops(cmn); + + /* create new NVS file */ + new_nvs = open(nvs_file, + O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (new_nvs < 0) { + fprintf(stderr, "%s> Unable to open new NVS file\n", __func__); + return 1; + } + + /* fill nvs part */ + if (nvs_upd_nvs_part(new_nvs, buf)) { + fprintf(stderr, "Fail to fill NVS part\n"); + res = 1; + + goto out; + } + + /* fill radio params */ + if (cmn->nvs_ops->nvs_set_fem_manuf(new_nvs, buf, val)) { + printf("Fail to fill radio params\n"); + res = 1; + } + +out: + close(new_nvs); + + return res; +} + +static void _print_hexa(char *name, unsigned char *data, size_t len) +{ + size_t i; + + printf("%s = ", name); + for (i = 0; i < len; i++) { + printf("%02X ", *data++); + } + printf("\n"); +} + +#define print_hexa(name, data) _print_hexa(name, data, sizeof(data)) + + +static void print_128x_general_params(struct wl128x_ini_general_params *p) +{ + printf("# SECTION 1.1: General parameters\n"); + printf("TXBiPFEMAutoDetect = %02X\n", p->tx_bip_fem_auto_detect); + printf("TXBiPFEMManufacturer = %02X\n", p->tx_bip_fem_manufacturer); + printf("RefClk = %02X\n", p->ref_clock); + printf("SettlingTime = %02X\n", p->settling_time); + printf("ClockValidOnWakeup = %02X\n", p->clk_valid_on_wakeup); + printf("TCXO_Clk = %02X\n", p->tcxo_ref_clock); + printf("TCXO_SettlingTime = %02X\n", p->tcxo_settling_time); + printf("TCXO_ClockValidOnWakeup = %02X\n", p->tcxo_valid_on_wakeup); + printf("TCXO_LDO_Voltage = %02X\n", p->tcxo_ldo_voltage); + printf("Platform_configuration = %02X\n", p->platform_conf); + printf("Single_Dual_Band_Solution = %02X\n", p->dual_mode_select); + print_hexa("Settings", p->general_settings); + printf("XTALItrimVal = %02X\n", p->xtal_itrim_val); + printf("SRState = %02X\n", p->sr_state); + print_hexa("SRF1", p->srf1); + print_hexa("SRF2", p->srf2); + print_hexa("SRF3", p->srf3); + printf("\n"); +} + +static void print_127x_general_params(struct wl1271_ini_general_params *p) +{ + printf("# SECTION 1.1: General parameters\n"); + printf("TXBiPFEMAutoDetect = %02X\n", p->tx_bip_fem_auto_detect); + printf("TXBiPFEMManufacturer = %02X\n", p->tx_bip_fem_manufacturer); + printf("RefClk = %02X\n", p->ref_clock); + printf("SettlingTime = %02X\n", p->settling_time); + printf("ClockValidOnWakeup = %02X\n", p->clk_valid_on_wakeup); + printf("DC2DCMode = %02X\n", p->dc2dc_mode); + printf("Single_Dual_Band_Solution = %02X\n", p->dual_mode_select); + printf("Settings = %02X\n", p->general_settings); + printf("SRState = %02X\n", p->sr_state); + print_hexa("SRF1", p->srf1); + print_hexa("SRF2", p->srf2); + print_hexa("SRF3", p->srf3); + printf("\n"); +} + +static void print_127x_band2_params(struct wl1271_ini_band_params_2 *p) +{ + printf("# SECTION 1.2.1: 2.4G parameters\n"); + printf("RxTraceInsertionLoss_2_4G = %02X\n", p->rx_trace_insertion_loss); + printf("TXTraceLoss_2_4G = %02X\n", p->tx_trace_loss); + print_hexa("RxRssiAndProcessCompensation_2_4G", p->rx_rssi_process_compens); + printf("\n"); +} + +static void print_128x_band2_params(struct wl128x_ini_band_params_2 *p) +{ + printf("# SECTION 1.2.1: 2.4G parameters\n"); + printf("RxTraceInsertionLoss_2_4G = %02X\n", p->rx_trace_insertion_loss); + print_hexa("TXTraceLoss_2_4G", p->tx_trace_loss); + printf("\n"); +} + +static void print_127x_band5_params(struct wl1271_ini_band_params_5 *p) +{ + printf("# SECTION 1.2.2: 5G parameters\n"); + + print_hexa("RxTraceInsertionLoss_5G", p->rx_trace_insertion_loss); + print_hexa("TXTraceLoss_5G", p->tx_trace_loss); + print_hexa("RxRssiAndProcessCompensation_5G", p->rx_rssi_process_compens); + printf("\n"); +} + +static void print_128x_band5_params(struct wl128x_ini_band_params_5 *p) +{ + printf("# SECTION 1.2.2: 5G parameters\n"); + + print_hexa("RxTraceInsertionLoss_5G", p->rx_trace_insertion_loss); + print_hexa("TXTraceLoss_5G", p->tx_trace_loss); + printf("\n"); +} + +static void _print_femhexa(char *fem ,char *name, unsigned char *data, int size) +{ + printf("%s", fem); + _print_hexa(name, data, size); +} + +static void _print_femle16a(char *fem, char *name, __le16 *data, int len) +{ + int i; + unsigned char *ptr = (unsigned char *)data; + + printf("%s%s = ", fem, name); + for (i = 0; i < len; i++) { + printf("%02X%02X ", *(ptr+1), *ptr); + ptr += 2; + } + printf("\n"); +} + + +#define print_femhexa(fem, name, data) _print_femhexa(fem, name, data, sizeof(data)) +#define print_femle16a(fem, name, data) _print_femle16a(fem, name, data, sizeof(data) / 2) + +static void print_127x_fem_param2(int femnr, struct wl1271_ini_fem_params_2 *p) +{ + char fem[6]; + int ret; + + printf("# SECTION 2.1.1: 2.4G parameters\n"); + + ret = snprintf(fem, sizeof(fem), "FEM%d_", femnr); + if (ret < 0 || ret >= ((int) sizeof(fem))) { + printf("# Invalid FEM numer %d\n", femnr); + return; + } + + printf("%sTXBiPReferencePDvoltage_2_4G = %04X\n", fem, p->tx_bip_ref_pd_voltage); + printf("%sTxBiPReferencePower_2_4G = %02X\n", fem, p->tx_bip_ref_power); + printf("%sTxBiPOffsetdB_2_4G = %02X\n", fem, p->tx_bip_ref_offset); + + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Normal", p->tx_per_rate_pwr_limits_normal); + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Degraded", p->tx_per_rate_pwr_limits_degraded); + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Extreme", p->tx_per_rate_pwr_limits_extreme); + + printf("%sDegradedLowToNormalThr_2_4G = %02X\n", fem, p->degraded_low_to_normal_thr); + printf("%sNormalToDegradedHighThr_2_4G = %02X\n", fem, p->normal_to_degraded_high_thr); + + print_femhexa(fem, "TxPerChannelPowerLimits_2_4G_11b", p->tx_per_chan_pwr_limits_11b); + print_femhexa(fem, "TxPerChannelPowerLimits_2_4G_OFDM", p->tx_per_chan_pwr_limits_ofdm); + print_femhexa(fem, "TxPDVsRateOffsets_2_4G", p->tx_pd_vs_rate_offsets); + print_femhexa(fem, "TxIbiasTable_2_4G", p->tx_ibias); + + printf("%sRxFemInsertionLoss_2_4G = %02X\n", fem, p->rx_fem_insertion_loss); +} + +static void print_128x_fem_param2(int femnr, struct wl128x_ini_fem_params_2 *p) +{ + char fem[6]; + sprintf(fem, "FEM%d_", femnr); + + printf("# SECTION 2.1.1: 2.4G parameters\n"); + + printf("%sTXBiPReferencePDvoltage_2_4G = %04X\n", fem, p->tx_bip_ref_pd_voltage); + printf("%sTxBiPReferencePower_2_4G = %02X\n", fem, p->tx_bip_ref_power); + printf("%sTxBiPOffsetdB_2_4G = %02X\n", fem, p->tx_bip_ref_offset); + + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Normal", p->tx_per_rate_pwr_limits_normal); + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Degraded", p->tx_per_rate_pwr_limits_degraded); + print_femhexa(fem, "TxPerRatePowerLimits_2_4G_Extreme", p->tx_per_rate_pwr_limits_extreme); + + printf("%sDegradedLowToNormalThr_2_4G = %02X\n", fem, p->degraded_low_to_normal_thr); + printf("%sNormalToDegradedHighThr_2_4G = %02X\n", fem, p->normal_to_degraded_high_thr); + + print_femhexa(fem, "TxPerChannelPowerLimits_2_4G_11b", p->tx_per_chan_pwr_limits_11b); + print_femhexa(fem, "TxPerChannelPowerLimits_2_4G_OFDM", p->tx_per_chan_pwr_limits_ofdm); + print_femhexa(fem, "TxPDVsRateOffsets_2_4G", p->tx_pd_vs_rate_offsets); + print_femhexa(fem, "TxPDVsChannelOffsets_2_4G", p->tx_pd_vs_chan_offsets); + print_femhexa(fem, "TxPDVsTemperature_2_4G", p->tx_pd_vs_temperature); + print_femhexa(fem, "TxIbiasTable_2_4G", p->tx_ibias); + + printf("%sRxFemInsertionLoss_2_4G = %02X\n", fem, p->rx_fem_insertion_loss); +} + +static void print_127x_fem_param5(int femnr, struct wl1271_ini_fem_params_5 *p) +{ + char fem[6]; + sprintf(fem, "FEM%d_", femnr); + + printf("# SECTION 2.1.2: 5G parameters\n"); + + print_femle16a(fem, "TxBiPReferencePDvoltage_5G", p->tx_bip_ref_pd_voltage); + + print_femhexa(fem, "TxBiPReferencePower_5G", p->tx_bip_ref_power); + print_femhexa(fem, "TxBiPOffsetdB_5G", p->tx_bip_ref_offset); + + print_femhexa(fem, "TxPerRatePowerLimits_5G_Normal", p->tx_per_rate_pwr_limits_normal); + print_femhexa(fem, "TxPerRatePowerLimits_5G_Degraded", p->tx_per_rate_pwr_limits_degraded); + print_femhexa(fem, "TxPerRatePowerLimits_5G_Extreme", p->tx_per_rate_pwr_limits_extreme); + + printf("%sDegradedLowToNormalThr_5G = %02X\n", fem, p->degraded_low_to_normal_thr); + printf("%sNormalToDegradedHighThr_5G = %02X\n", fem, p->normal_to_degraded_high_thr); + + print_femhexa(fem, "TxPerChannelPowerLimits_5G_OFDM", p->tx_per_chan_pwr_limits_ofdm); + print_femhexa(fem, "TxPDVsRateOffsets_5G", p->tx_pd_vs_rate_offsets); + print_femhexa(fem, "TxIbiasTable_5G", p->tx_ibias); + print_femhexa(fem, "RxFemInsertionLoss_5G", p->rx_fem_insertion_loss); +} + +static void print_128x_fem_param5(int femnr, struct wl128x_ini_fem_params_5 *p) +{ + char fem[6]; + sprintf(fem, "FEM%d_", femnr); + + printf("# SECTION 2.1.2: 5G parameters\n"); + + print_femle16a(fem, "TxBiPReferencePDvoltage_5G", p->tx_bip_ref_pd_voltage); + + print_femhexa(fem, "TxBiPReferencePower_5G", p->tx_bip_ref_power); + print_femhexa(fem, "TxBiPOffsetdB_5G", p->tx_bip_ref_offset); + + print_femhexa(fem, "TxPerRatePowerLimits_5G_Normal", p->tx_per_rate_pwr_limits_normal); + print_femhexa(fem, "TxPerRatePowerLimits_5G_Degraded", p->tx_per_rate_pwr_limits_degraded); + print_femhexa(fem, "TxPerRatePowerLimits_5G_Extreme", p->tx_per_rate_pwr_limits_extreme); + + printf("%sDegradedLowToNormalThr_5G = %02X\n", fem, p->degraded_low_to_normal_thr); + printf("%sNormalToDegradedHighThr_5G = %02X\n", fem, p->normal_to_degraded_high_thr); + + print_femhexa(fem, "TxPerChannelPowerLimits_5G_OFDM", p->tx_per_chan_pwr_limits_ofdm); + print_femhexa(fem, "TxPDVsRateOffsets_5G", p->tx_pd_vs_rate_offsets); + print_femhexa(fem, "TxPDVsChannelOffsets_5G", p->tx_pd_vs_chan_offsets); + print_femhexa(fem, "TxPDVsTemperature_5G", p->tx_pd_vs_temperature); + print_femhexa(fem, "TxIbiasTable_5G", p->tx_ibias); + print_femhexa(fem, "RxFemInsertionLoss_5G", p->rx_fem_insertion_loss); +} + +int get_fem_nr(int autodetect, int manuf, int *femcnt, int *femi) +{ + if (autodetect) { + printf("#Fem autodetect is on. Showing both FEM datas\n"); + *femcnt = 2; + *femi = 0; + } + else { + *femcnt = 1; + if(manuf >= WL1271_INI_FEM_MODULE_COUNT) { + fprintf(stderr, "FEM index out of bounds (%d > %d)\n", manuf, + WL1271_INI_FEM_MODULE_COUNT); + return 1; + } + + *femi = manuf; + printf("#Fem autodetect is off. FEM manufacturer=%d " + "Fem entry used is %d\n", + manuf, WL12XX_FEM_TO_NVS_ENTRY(manuf)); + } + return 0; +} + + + +int info_nvs_file(const char *nvs_file) +{ + char buf[BUF_SIZE_4_NVS_FILE]; + int ret, i, femi, femcnt, maxfem, fem_idx; + + int fd = open(nvs_file, O_RDONLY, S_IRUSR | S_IWUSR); + if (fd < 0) { + fprintf(stderr, "Unable to open NVS %s\n", nvs_file); + return 1; + } + + ret = read(fd, buf, BUF_SIZE_4_NVS_FILE); + if (ret < 0) { + fprintf(stderr, "Fail to read file %s (%s)\n", nvs_file, + strerror(errno)); + close(fd); + return 1; + } + close(fd); + + if (ret == sizeof(struct wl1271_nvs_file)) { + struct wl1271_nvs_file *nvs = (struct wl1271_nvs_file *) &buf; + printf("#Chip is 127x\n"); + print_127x_general_params(&nvs->ini.general_params); + print_127x_band2_params(&nvs->ini.stat_radio_params_2); + if (nvs->ini.general_params.dual_mode_select) + print_127x_band5_params(&nvs->ini.stat_radio_params_5); + + if( get_fem_nr(nvs->ini.general_params.tx_bip_fem_auto_detect, + nvs->ini.general_params.tx_bip_fem_manufacturer, + &femcnt, &femi)) + return 1; + + maxfem = femcnt + femi; + for (i = femi; i < maxfem; i++) { + fem_idx = WL12XX_FEM_TO_NVS_ENTRY(i); + + print_127x_fem_param2(i, &nvs->ini. + dyn_radio_params_2[fem_idx].params); + printf("\n"); + + if (nvs->ini.general_params.dual_mode_select == 1) { + print_127x_fem_param5(femi, &nvs->ini. + dyn_radio_params_5[fem_idx].params); + printf("\n"); + } + femi++; + } + } + else if (ret == sizeof(struct wl128x_nvs_file)) { + struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *) &buf; + printf("#Chip is 128x\n"); + print_128x_general_params(&nvs->ini.general_params); + print_128x_band2_params(&nvs->ini.stat_radio_params_2); + if (nvs->ini.general_params.dual_mode_select) + print_128x_band5_params(&nvs->ini.stat_radio_params_5); + + printf("# SECTION 2.1: FEM parameters\n"); + printf("FemVendorAndOptions = %02X\n\n", nvs->ini.fem_vendor_and_options); + + if( get_fem_nr(nvs->ini.general_params.tx_bip_fem_auto_detect, + nvs->ini.general_params.tx_bip_fem_manufacturer, + &femcnt, &femi)) + return 1; + + maxfem = femcnt + femi; + for (i = femi; i < maxfem; i++) { + fem_idx = WL12XX_FEM_TO_NVS_ENTRY(i); + + print_128x_fem_param2(femi, &nvs->ini. + dyn_radio_params_2[fem_idx].params); + printf("\n"); + if (nvs->ini.general_params.dual_mode_select == 1) { + print_128x_fem_param5(femi, &nvs->ini. + dyn_radio_params_5[fem_idx].params); + printf("\n"); + } + femi++; + } + } + else { + fprintf(stderr, "Invalid file size %d. Unable to detect chip type\n", ret); + return 0; + } + + return 0; +} + diff --git a/nvs.h b/nvs.h new file mode 100644 index 0000000..481fbe2 --- /dev/null +++ b/nvs.h @@ -0,0 +1,31 @@ +#ifndef __NVS_H +#define __NVS_H + +#define WL127X_NVS_FILE_SZ 912 +#define WL128X_NVS_FILE_SZ 1113 + +char *get_opt_nvsinfile(int argc, char **argv); +char *get_opt_nvsoutfile(int argc, char **argv); + +int prepare_nvs_file(void *arg, char *file_name); + +int nvs_set_mac(char *nvsfile, char *mac); + +void cfg_nvs_ops(struct wl12xx_common *cmn); + +int create_nvs_file(struct wl12xx_common *cmn); + +int update_nvs_file(const char *nvs_infile, const char *nvs_outfile, + struct wl12xx_common *cmn); + +int dump_nvs_file(const char *nvs_file); + +int set_nvs_file_autofem(const char *nvs_file, unsigned char val, + struct wl12xx_common *cmn); + +int set_nvs_file_fem_manuf(const char *nvs_file, unsigned char val, + struct wl12xx_common *cmn); + +int info_nvs_file(const char *nvs_file); + +#endif /* __NVS_H */ diff --git a/plt.c b/plt.c new file mode 100644 index 0000000..1964b9d --- /dev/null +++ b/plt.c @@ -0,0 +1,1431 @@ +/* + * PLT utility for wireless chip supported by TI's driver wl12xx + * + * See README and COPYING for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include "nl80211.h" + +#include "calibrator.h" +#include "plt.h" +#include "ini.h" +#include "nvs.h" + +#define ZERO_MAC "00:00:00:00:00:00" + +#ifndef SIOCETHTOOL +#define SIOCETHTOOL 0x8946 +#endif + +SECTION(plt); + +#define CMDBUF_SIZE 200 +static int insmod(char *filename) +{ + int ret; + char cmd[CMDBUF_SIZE]; + snprintf(cmd, CMDBUF_SIZE, "%s %s", INSMOD_PATH, filename); + ret = system(cmd); + if (ret) + fprintf(stderr, "Failed to load kernel module using command %s\n", cmd); + return ret; +} + +static int rmmod(char *name) +{ + char cmd[CMDBUF_SIZE]; + char *tmp; + int i, ret; + + /* "basename" */ + tmp = strrchr(name, '/'); + if (!tmp) + tmp = name; + else + tmp++; + + tmp = strdup(tmp); + if (!tmp) + return -ENOMEM; + + /* strip trailing .ko if there */ + i = strlen(tmp); + if (i < 4) { + ret = -EINVAL; + goto out; + } + if (!strcmp(tmp + i - 3, ".ko")) + tmp[i-3] = 0; + + snprintf(cmd, CMDBUF_SIZE, "%s %s", RMMOD_PATH, tmp); + ret = system(cmd); + if (ret) + fprintf(stderr, "Failed to remove kernel module using command %s\n", cmd); +out: + free(tmp); + return ret; +} + +static int fem_detect_valid_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + unsigned char *fem_manuf; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + fem_manuf = (unsigned char*) nla_data(td[WL1271_TM_ATTR_DATA]); + + printf("Firmware detect FEM type=%d\n", *fem_manuf); + + return NL_SKIP; +} + +static int plt_power_mode(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + unsigned int pmode; + + if (argc != 1) { + fprintf(stderr, "%s> Missing arguments\n", __func__); + return 2; + } + + if (strcmp(argv[0], "on") == 0) + pmode = PLT_ON; + else if (strcmp(argv[0], "off") == 0) + pmode = PLT_OFF; + else if (strcmp(argv[0], "fem_detect") == 0) + pmode = PLT_FEM_DETECT; + else if (strcmp(argv[0], "chip_awake") == 0) + pmode = PLT_CHIP_AWAKE; + else { + fprintf(stderr, "%s> Invalid parameter\n", __func__); + return 2; + } + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_SET_PLT_MODE); + NLA_PUT_U32(msg, WL1271_TM_ATTR_PLT_MODE, pmode); + + nla_nest_end(msg, key); + + if (pmode == PLT_FEM_DETECT) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, fem_detect_valid_handler, NULL); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, power_mode, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_power_mode, + "Set PLT power mode\n"); + +static int plt_tune_channel(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_cal_channel_tune prms; + + if (argc < 1 || argc > 2) + return 1; + + prms.test.id = TEST_CMD_CHANNEL_TUNE; + prms.band = (unsigned char)atoi(argv[0]); + prms.channel = (unsigned char)atoi(argv[1]); + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, + sizeof(struct wl1271_cmd_cal_channel_tune), + &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, tune_channel, " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_tune_channel, + "Set band and channel for PLT\n"); + +static int plt_ref_point(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_cal_update_ref_point prms; + + if (argc < 1 || argc > 3) + return 1; + + prms.test.id = TEST_CMD_UPDATE_PD_REFERENCE_POINT; + prms.ref_detector = atoi(argv[0]); + prms.ref_power = atoi(argv[1]); + prms.sub_band = atoi(argv[2]); + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, ref_point, " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_ref_point, + "Set reference point for PLT\n"); + +static int calib_valid_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + struct wl1271_cmd_cal_p2g *prms; +#if 0 + int i; unsigned char *pc; +#endif + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + prms = (struct wl1271_cmd_cal_p2g *)nla_data(td[WL1271_TM_ATTR_DATA]); + + if (prms->radio_status) { + fprintf(stderr, "Fail to calibrate with radio status (%d)\n", + (signed short)prms->radio_status); + return 2; + } +#if 0 + printf("%s> id %04x status %04x\ntest id %02x ver %08x len %04x=%d\n", + __func__, + prms->header.id, prms->header.status, prms->test.id, + prms->ver, prms->len, prms->len); + + pc = (unsigned char *)prms->buf; + printf("++++++++++++++++++++++++\n"); + for (i = 0; i < prms->len; i++) { + if (i%0xf == 0) + printf("\n"); + + printf("%02x ", *(unsigned char *)pc); + pc += 1; + } + printf("++++++++++++++++++++++++\n"); +#endif + printf("Writing calibration data to %s\n", (char*) arg); + if (prepare_nvs_file(prms, arg)) { + fprintf(stderr, "Fail to prepare calibrated NVS file\n"); + return 2; + } +#if 0 + printf("\n\tThe NVS file (%s) is ready\n\tCopy it to %s and " + "reboot the system\n\n", + NEW_NVS_NAME, CURRENT_NVS_NAME); +#endif + return NL_SKIP; +} + +static void dump_regs(struct ethtool_drvinfo *info, struct ethtool_regs *regs) +{ + printf("\n\tDriver %s\n\t" + "version %s\n\t" + "FW version %s\n\t" + "Bus info %s\n\t" + "HW version 0x%X\n", + info->driver, info->version, + info->fw_version, info->bus_info, regs->version); +} + + +int do_get_drv_info(char *dev_name, int *hw_ver, + struct ethtool_drvinfo *out_drvinfo) +{ + struct ifreq ifr; + int fd, err; + struct ethtool_drvinfo drvinfo; + struct ethtool_regs *regs; + + memset(&ifr, 0, sizeof(ifr)); + strcpy(ifr.ifr_name, dev_name); + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + fprintf(stderr, "Cannot get control socket\n"); + return 1; + } + + drvinfo.cmd = ETHTOOL_GDRVINFO; + ifr.ifr_data = (caddr_t)&drvinfo; + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + fprintf(stderr, "Cannot get driver information\n"); + goto error_out; + } + + regs = calloc(1, sizeof(*regs)+drvinfo.regdump_len); + if (!regs) { + fprintf(stderr, "Cannot allocate memory for register dump\n"); + goto error_out; + } + + regs->cmd = ETHTOOL_GREGS; + regs->len = drvinfo.regdump_len; + ifr.ifr_data = (caddr_t)regs; + err = ioctl(fd, SIOCETHTOOL, &ifr); + if (err < 0) { + fprintf(stderr, "Cannot get register dump\n"); + goto error_out2; + } + + if (hw_ver) + *hw_ver = regs->version; + if (out_drvinfo) + *out_drvinfo=drvinfo; + if (!hw_ver && !out_drvinfo) + dump_regs(&drvinfo, regs); + free(regs); + + return 0; + +error_out2: + free(regs); + +error_out: + close(fd); + + return 1; +} + +int is_fw_ver_valid(char *dev_name, struct fw_version *fw_ver_valid) +{ + char *str, *tmp_str; + struct ethtool_drvinfo drvinfo; + int i=0, ret=0; + struct fw_version fw_ver_crnt; + + ret = do_get_drv_info(dev_name, NULL, &drvinfo); + if(ret) { + printf("\tFailed to get FW version.\n"); + goto error; + } + + str=drvinfo.fw_version; + + /* Looking for the last space in the version.*/ + /* Usually version starting with text and value after spacing.*/ + tmp_str = strrchr (str,' '); + while((tmp_str != NULL) && + (i < (int) (sizeof(fw_ver_valid->ver)/sizeof(int)))) + { + tmp_str++; + fw_ver_crnt.ver[i]=atoi(tmp_str); + if (fw_ver_crnt.ver[i] < fw_ver_valid->ver[i]) { + ret=1; + break; + } else if (fw_ver_crnt.ver[i] > fw_ver_valid->ver[i]) { + ret=0; + break; + } + tmp_str = strchr (tmp_str, '.'); + i++; + } + +error: + return ret; +} + +static int get_chip_arch(char *dev_name, enum wl12xx_arch *arch) +{ + int hw_ver, ret; + + ret = do_get_drv_info(dev_name, &hw_ver, NULL); + if (ret) + return 1; + + *arch = hw_ver >> 16; + + return 0; +} + +static int do_nvs_ver21(struct nl_msg *msg, enum wl12xx_arch arch) +{ + struct nlattr *key; + struct wl1271_cmd_set_nvs_ver prms; + + memset(&prms, 0, sizeof(struct wl1271_cmd_set_nvs_ver)); + + if (arch == WL1271_ARCH) + prms.test.id = TEST_CMD_SET_NVS_VERSION; + else if(arch == WL128X_ARCH) + prms.test.id = TEST_CMD_SET_NVS_VERSION - 1; + else { + fprintf(stderr, "Unkown arch %x\n", arch); + return 1; + } + + prms.nvs_ver = NVS_VERSION_2_1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + + +static int plt_nvs_ver(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + enum wl12xx_arch arch = UNKNOWN_ARCH; + int ret; + + if (argc < 1) { + fprintf(stderr, "Missing device name\n"); + return 2; + } + + ret = get_chip_arch(argv[0], &arch); + if (ret || (arch == UNKNOWN_ARCH)) { + fprintf(stderr, "Unknown chip arch\n"); + return 2; + } + + return do_nvs_ver21(msg, arch); +} + +COMMAND(plt, nvs_ver, "", + NL80211_CMD_TESTMODE, 0, CIB_PHY, plt_nvs_ver, + "Set NVS version\n"); + + +static int plt_nvs_ver2(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + enum wl12xx_arch arch = UNKNOWN_ARCH; + int ret; + + if (argc < 1) { + fprintf(stderr, "Missing device name\n"); + return 2; + } + + ret = sscanf(argv[0], "%x", &arch); + if(ret != 1) { + fprintf(stderr, "Unknown chip arch\n"); + return 2; + } + + return do_nvs_ver21(msg, arch); +} + +COMMAND(plt, nvs_ver, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_nvs_ver2, + "Set NVS version\n"); + +static int plt_tx_bip(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_cal_p2g prms; + int i; + char *nvs_path; + static char void_path[] = {'\0'}; + + if (argc < 8) { + fprintf(stderr, "%s> Missing arguments\n", __func__); + return 2; + } + + if (argc > 8) + nvs_path = argv[8]; + else + nvs_path = void_path; + + memset(&prms, 0, sizeof(struct wl1271_cmd_cal_p2g)); + + prms.test.id = TEST_CMD_P2G_CAL; + for (i = 0; i < 8; i++) + prms.sub_band_mask |= (atoi(argv[i]) & 0x1)< building message failed\n", __func__); + return 2; +} + +COMMAND(plt, tx_bip, + "<0|1> <0|1> <0|1> <0|1> <0|1> <0|1> <0|1> <0|1> []", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_tx_bip, + "Do calibrate\n"); + +static int plt_tx_tone(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_cal_tx_tone prms; + + if (argc < 2) { + fprintf(stderr, "%s> Missing arguments\n", __func__); + return 2; + } + + memset(&prms, 0, sizeof(struct wl1271_cmd_cal_tx_tone)); + + prms.test.id = TEST_CMD_TELEC; + + prms.tone_type = atoi(argv[0]); + if (prms.tone_type < 1 || prms.tone_type > 2) { + fprintf(stderr, "%s> Invalit tone type parameter %d\n", + __func__, prms.tone_type); + return 2; + } + + prms.power = atoi(argv[1]); + if (prms.power > 10000) { + fprintf(stderr, "%s> Invalit power parameter %d\n", + __func__, prms.power); + return 2; + } + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, tx_tone, " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_tx_tone, + "Do command tx_tone to transmit a tone\n"); + +static int plt_tx_cont(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_pkt_params prms = { + .src_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + }; + + if (argc != 15) + return 1; +#if 0 + printf("%s> delay (%d) rate (%08x) size (%d) amount (%d) power (%d) " + "seed (%d) pkt_mode (%d) DCF (%d) GI (%d) preamble (%d) type " + "(%d) scramble (%d) CLPC (%d), SeqNbrMode (%d) DestMAC (%s)\n", + __func__, + atoi(argv[0]), atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), + atoi(argv[4]), atoi(argv[5]), atoi(argv[6]), atoi(argv[7]), + atoi(argv[8]), atoi(argv[9]), atoi(argv[10]), atoi(argv[11]), + atoi(argv[12]), atoi(argv[13]), argv[14] + ); +#endif + memset((void *)&prms, 0, sizeof(struct wl1271_cmd_pkt_params)); + + prms.test.id = TEST_CMD_FCC; + prms.delay = atoi(argv[0]); + prms.rate = strtol(argv[1], NULL, 0); + prms.size = (unsigned short)atoi(argv[2]); + prms.amount = (unsigned short)atoi(argv[3]); + prms.power = atoi(argv[4]); + prms.seed = (unsigned short)atoi(argv[5]); + prms.pkt_mode = (unsigned char)atoi(argv[6]); + prms.dcf_enable = (unsigned char)atoi(argv[7]); + prms.g_interval = (unsigned char)atoi(argv[8]); + prms.preamble = (unsigned char)atoi(argv[9]); + prms.type = (unsigned char)atoi(argv[10]); + prms.scramble = (unsigned char)atoi(argv[11]); + prms.clpc_enable = (unsigned char)atoi(argv[12]); + prms.seq_nbr_mode = (unsigned char)atoi(argv[13]); + str2mac(prms.dst_mac, argv[14]); + + if (get_mac_addr(0, prms.src_mac)) + fprintf(stderr, "fail to get MAC addr\n"); + + printf("%02X:%02X:%02X:%02X:%02X:%02X\n", + prms.src_mac[0], prms.src_mac[1], prms.src_mac[2], + prms.src_mac[3], prms.src_mac[4], prms.src_mac[5]); + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, tx_cont, " \n\t\t " + " \n\t\t " + " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_tx_cont, + "Start Tx Cont\n"); + +static int plt_tx_stop(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_pkt_params prms; + + prms.test.id = TEST_CMD_STOP_TX; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, tx_stop, NULL, + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_tx_stop, + "Stop Tx Cont\n"); + +static int plt_start_rx_statcs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_pkt_params prms; + + prms.test.id = TEST_CMD_RX_STAT_START; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, start_rx_statcs, NULL, + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_start_rx_statcs, + "Start Rx statistics collection\n"); + +static int plt_stop_rx_statcs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_pkt_params prms; + + prms.test.id = TEST_CMD_RX_STAT_STOP; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, stop_rx_statcs, NULL, + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_stop_rx_statcs, + "Stop Rx statistics collection\n"); + +static int plt_reset_rx_statcs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_cmd_pkt_params prms; + + prms.test.id = TEST_CMD_RX_STAT_RESET; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, reset_rx_statcs, NULL, + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_reset_rx_statcs, + "Reset Rx statistics collection\n"); + +static int display_rx_statcs(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + struct wl1271_radio_rx_statcs *prms; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + prms = + (struct wl1271_radio_rx_statcs *) + nla_data(td[WL1271_TM_ATTR_DATA]); + + printf("\n\tTotal number of pkts\t- %d\n\tAccepted pkts\t\t- %d\n\t" + "FCS error pkts\t\t- %d\n\tAddress mismatch pkts\t- %d\n\t" + "Average SNR\t\t- % d dBm\n\tAverage RSSI\t\t- % d dBm\n\n", + prms->base_pkt_id, prms->rx_path_statcs.nbr_rx_valid_pkts, + prms->rx_path_statcs.nbr_rx_fcs_err_pkts, + prms->rx_path_statcs.nbr_rx_plcp_err_pkts, + (signed short)prms->rx_path_statcs.ave_snr/8, + (signed short)prms->rx_path_statcs.ave_rssi/8); + + return NL_SKIP; +} + +static int plt_get_rx_statcs(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl1271_radio_rx_statcs prms; + + prms.test.id = TEST_CMD_RX_STAT_GET; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + NLA_PUT_U8(msg, WL1271_TM_ATTR_ANSWER, 1); + + nla_nest_end(msg, key); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, display_rx_statcs, NULL); + + /* Important: needed gap between tx_start and tx_get */ + sleep(2); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(plt, get_rx_statcs, NULL, + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_get_rx_statcs, + "Get Rx statistics\n"); + +static int plt_rx_statistics(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + int ret; + + /* power mode on */ + { + char *prms[4] = { "wlan0", "plt", "power_mode", "on" }; + + ret = handle_cmd(state, II_NETDEV, 4, prms); + if (ret < 0) { + fprintf(stderr, "Fail to set PLT power mode on\n"); + return 1; + } + } + + /* start_rx_statcs */ + { + char *prms[3] = { "wlan0", "plt", "start_rx_statcs" }; + + ret = handle_cmd(state, II_NETDEV, 3, prms); + if (ret < 0) { + fprintf(stderr, "Fail to start Rx statistics\n"); + goto fail_out; + } + } + + /* get_rx_statcs */ + { + int err; + char *prms[3] = { "wlan0", "plt", "get_rx_statcs" }; + + err = handle_cmd(state, II_NETDEV, 3, prms); + if (err < 0) { + fprintf(stderr, "Fail to get Rx statistics\n"); + ret = err; + } + } + + + /* stop_rx_statcs */ + { + int err; + char *prms[3] = { "wlan0", "plt", "stop_rx_statcs" }; + + err = handle_cmd(state, II_NETDEV, 3, prms); + if (err < 0) { + fprintf(stderr, "Fail to stop Rx statistics\n"); + ret = err; + } + } + +fail_out: + /* power mode off */ + { + int err; + char *prms[4] = { "wlan0", "plt", "power_mode", "off"}; + + err = handle_cmd(state, II_NETDEV, 4, prms); + if (err < 0) { + fprintf(stderr, "Fail to set PLT power mode on\n"); + return 1; + } + } + + if (ret < 0) + return 1; + + return 0; +} + +COMMAND(plt, rx_statistics, NULL, 0, 0, CIB_NONE, plt_rx_statistics, + "Get Rx statistics\n"); + +static int plt_do_power_on(struct nl80211_state *state, char *devname) +{ + int err; + char *pm_on[4] = { devname, "plt", "power_mode", "on" }; + + err = handle_cmd(state, II_NETDEV, ARRAY_SIZE(pm_on), pm_on); + if (err < 0) + fprintf(stderr, "Fail to set PLT power mode on\n"); + + return err; +} + +static int plt_do_power_off(struct nl80211_state *state, char *devname) +{ + int err; + char *prms[4] = { devname, "plt", "power_mode", "off"}; + + err = handle_cmd(state, II_NETDEV, ARRAY_SIZE(prms), prms); + if (err < 0) + fprintf(stderr, "Failed to set PLT power mode on\n"); + + return err; +} + + +static int plt_do_calibrate(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int single_dual, char *nvs_file, + char *devname, enum wl12xx_arch arch) +{ + int ret = 0, err; + + /* tune channel */ + { + char *tune[5] = { + devname, "plt", "tune_channel", "0", "7" + }; + + err = handle_cmd(state, II_NETDEV, ARRAY_SIZE(tune), tune); + if (err < 0) { + fprintf(stderr, "Fail to tune channel\n"); + ret = err; + goto fail_out; + } + } + + /* Set nvs version 2.1 */ + if (arch == UNKNOWN_ARCH) { + fprintf(stderr, "Unknown arch. Not setting nvs ver 2.1\n"); + } + else { + size_t ret; + char archstr[5] = ""; + char *prms[4] = { + "wlan0", "plt", "nvs_ver", archstr + }; + + ret = snprintf(archstr, sizeof(archstr), "%x", arch); + if (ret > sizeof(archstr)) { + fprintf(stderr, "Bad arch\n"); + goto fail_out; + } + + printf("Using nvs version 2.1\n"); + err = handle_cmd(state, II_NETDEV, 4, prms); + if (err < 0) { + fprintf(stderr, "Fail to set nvs ver 2.1\n"); + ret = err; + } + } + + /* calibrate it */ + { + char *prms[12] = { + devname, "plt", "tx_bip", "1", "0", "0", "0", + "0", "0", "0", "0", nvs_file + }; + printf("Calibrate %s\n", nvs_file); + + /* set flags in case of dual band */ + if (single_dual) { + prms[4] = prms[5] = prms[6] = prms[7] = prms[8] = + prms[9] = prms[10] = "1"; + } + + err = handle_cmd(state, II_NETDEV, ARRAY_SIZE(prms), prms); + if (err < 0) { + fprintf(stderr, "Failed to calibrate\n"); + ret = err; + } + } + +fail_out: + if (ret < 0) + return 1; + + return 0; +} + +static int plt_calibrate(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + int ret, err; + int single_dual = 0; + + if (argc > 2 && (strncmp(argv[2], "dual", 4) == 0)) + single_dual = 1; /* going for dual band calibration */ + else + single_dual = 0; /* going for single band calibration */ + + + err = plt_do_power_on(state, "wlan0"); + if (err < 0) + goto out; + + err = plt_do_calibrate(state, cb, msg, single_dual, NEW_NVS_NAME, + "wlan0", UNKNOWN_ARCH); + + ret = plt_do_power_off(state, "wlan0"); + if (ret < 0) + err = ret; +out: + return err; +} + +COMMAND(plt, calibrate, "[]", 0, 0, CIB_NONE, + plt_calibrate, "Do calibrate for single or dual band chip\n"); + + +static int plt_autocalibrate(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct wl12xx_common cmn = { + .auto_fem = 0, + .arch = UNKNOWN_ARCH, + .parse_ops = NULL, + }; + + char *devname, *modpath, *inifile1, *macaddr; + char *set_mac_prms[5]; + int single_dual = 0, res, fems_parsed; + + argc -= 2; + argv += 2; + + if (argc < 4 || argc > 5) { + return 1; + } + + devname = *argv++; + argc--; + + modpath = *argv++; + argc--; + + inifile1 = *argv++; + argc--; + + cmn.nvs_name = get_opt_nvsoutfile(argc--, argv++); + + if (argc) { + macaddr = *argv++; + argc--; + } else { + macaddr = NULL; + } + + if (file_exist(cmn.nvs_name) >= 0) { + fprintf(stderr, "nvs file %s. File already exists. Won't overwrite.\n", cmn.nvs_name); + return 0; + } + + /* Create ref nvs */ + if (read_ini(inifile1, &cmn)) { + fprintf(stderr, "Failed to read ini file %s\n", inifile1); + goto out_removenvs; + } + + fems_parsed = cmn.fem0_bands + cmn.fem1_bands + cmn.fem2_bands + cmn.fem3_bands; + + /* Get nr bands from parsed ini */ + single_dual = ini_get_dual_mode(&cmn); + + if (single_dual == 0) { + if (fems_parsed < 1 || fems_parsed > 4) { + fprintf(stderr, "Incorrect number of FEM sections %d for single mode\n", + fems_parsed); + return 1; + } + } + else if (single_dual == 1) { + if (fems_parsed < 2 && fems_parsed > 8) { + fprintf(stderr, "Incorrect number of FEM sections %d for dual mode\n", + fems_parsed); + return 1; + } + } + else { + fprintf(stderr, "Invalid value for TXBiPFEMAutoDetect %d\n", + single_dual); + return 1; + } + + /* I suppose you can have one FEM with 2.4 only and one in dual band + but it's more likely a mistake. + + In normal situation we have: + ---------------------------- + Single Band Manual - 1 FEM + Dual Band Manual - 2 FEMs + Single Band Auto Fem - 4 FEMs + Dual Band Auto Fem - 8 FEMs */ + if ((single_dual + 1) * (cmn.auto_fem * 3 + 1) != fems_parsed) { + printf("WARNING: %d FEMS for %d bands with autofem %s looks " + "like a strange configuration\n", + fems_parsed, single_dual + 1, + cmn.auto_fem ? "on" : "off"); + } + + cfg_nvs_ops(&cmn); + + if (create_nvs_file(&cmn)) { + fprintf(stderr, "Failed to create reference NVS file\n"); + return 1; + } + + /* Load module */ + res = insmod(modpath); + if (res) { + goto out_removenvs; + } + + res = plt_do_power_on(state, devname); + if (res < 0) + goto out_rmmod; + + res = plt_do_calibrate(state, cb, msg, single_dual, + cmn.nvs_name, devname, cmn.arch); + if (res) { + goto out_power_off; + } + + set_mac_prms[0] = devname; + set_mac_prms[1] = "plt"; + set_mac_prms[2] = "set_mac"; + set_mac_prms[3] = cmn.nvs_name; + set_mac_prms[4] = macaddr; + + res = handle_cmd(state, II_NETDEV, + ARRAY_SIZE(set_mac_prms) - (!macaddr), + set_mac_prms); + if (res) { + goto out_power_off; + } + + /* we can ignore the return value, because we rmmod anyway */ + plt_do_power_off(state, devname); + rmmod(modpath); + + printf("Calibration done. "); + if (cmn.fem0_bands) { + printf("FEM0 has %d bands. ", cmn.fem0_bands); + } + if (cmn.fem1_bands) { + printf("FEM1 has %d bands. ", cmn.fem1_bands); + } + if (cmn.fem2_bands) { + printf("FEM2 has %d bands. ", cmn.fem2_bands); + } + if (cmn.fem3_bands) { + printf("FEM3 has %d bands. ", cmn.fem3_bands); + } + + printf("AutoFEM is %s. ", cmn.auto_fem ? "on" : "off"); + + printf("Resulting nvs is %s\n", + cmn.nvs_name); + return 0; + +out_power_off: + /* we can ignore the return value, because we rmmod anyway */ + plt_do_power_off(state, devname); +out_rmmod: + rmmod(modpath); + +out_removenvs: + fprintf(stderr, "Calibration not complete. Removing half-baked nvs\n"); + unlink(cmn.nvs_name); + return 0; + +} +COMMAND(plt, autocalibrate, " " + "[|from_fuse|default]", 0, 0, CIB_NONE, plt_autocalibrate, + "Do automatic calibration.\n" + "The MAC address value can be:\n" + "from_fuse\ttry to read from the fuse ROM, if not available the command fails\n" + "default\t\twrite 00:00:00:00:00:00 to have the driver read from the fuse ROM,\n" + "\t\t\tfails if not available\n" + "00:00:00:00:00:00\tforce use of a zeroed MAC address (use with caution!)\n"); + +static int plt_get_mac_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + char *addr; + int lower; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + addr = (char *) nla_data(td[WL1271_TM_ATTR_DATA]); + + printf("BD_ADDR from fuse:\t0x%0x:0x%0x:0x%0x:0x%0x:0x%0x:0x%0x\n", + addr[0], addr[1], addr[2], + addr[3], addr[4], addr[5]); + + lower = (addr[3] << 16) + (addr[4] << 8) + addr[5]; + + lower++; + printf("First WLAN MAC:\t\t0x%0x:0x%0x:0x%0x:0x%0x:0x%0x:0x%0x\n", + addr[0], addr[1], addr[2], + (lower & 0xff0000) >> 16, + (lower & 0xff00) >> 8, + (lower & 0xff)); + + lower++; + printf("Second WLAN MAC:\t0x%0x:0x%0x:0x%0x:0x%0x:0x%0x:0x%0x\n", + addr[0], addr[1], addr[2], + (lower & 0xff0000) >> 16, + (lower & 0xff00) >> 8, + (lower & 0xff)); + + return NL_SKIP; +} + +static int plt_get_mac_from_fuse(struct nl_msg *msg, struct nl_cb *cb, + nl_recvmsg_msg_cb_t callback, void *arg) +{ + struct nlattr *key; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "%s> fail to nla_nest_start()\n", __func__); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_GET_MAC); + + nla_nest_end(msg, key); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, arg); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +static int plt_get_mac(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + if (argc != 0) + return 1; + + return plt_get_mac_from_fuse(msg, cb, plt_get_mac_cb, NULL); +} +COMMAND(plt, get_mac, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_get_mac, + "Read MAC address from the Fuse ROM.\n"); + +static int plt_set_mac_from_fuse_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + char mac[sizeof(ZERO_MAC)]; + char *addr; + char *nvs_file = (char *) arg; + int lower; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + addr = (char *) nla_data(td[WL1271_TM_ATTR_DATA]); + + /* + * The first address is the BD_ADDR, the next is the first + * MAC. Increment only the lower part, so we don't overflow + * to the OUI */ + lower = (addr[3] << 16) + (addr[4] << 8) + addr[5] + 1; + + snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x", + addr[0], addr[1], addr[2], (lower & 0xff0000) >> 16, + (lower & 0xff00) >> 8, (lower & 0xff)); + + /* ignore the return value, since a message was already printed out */ + nvs_set_mac(nvs_file, mac); + + return NL_SKIP; +} + +static int plt_set_mac_default_cb(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + char *nvs_file = (char *) arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + /* + * No need to parse, we just need to know if the command + * worked (ie. the hardware supports MAC from fuse) so the + * driver can fetch it by itself. + */ + + /* ignore the return value, since a message was already printed out */ + nvs_set_mac(nvs_file, ZERO_MAC); + + return NL_SKIP; +} + +static int plt_set_mac_from_fuse(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + return plt_get_mac_from_fuse(msg, cb, plt_set_mac_from_fuse_cb, argv[0]); +} +HIDDEN(plt, set_mac_from_fuse, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_set_mac_from_fuse); + +static int plt_set_mac_default(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + return plt_get_mac_from_fuse(msg, cb, plt_set_mac_default_cb, argv[0]); +} +HIDDEN(plt, set_mac_default, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_set_mac_default); + +static int plt_set_mac(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + char *nvs_file; + + if (argc < 4 || argc > 5) + return 1; + + nvs_file = argv[3]; + + if (argc == 4 || !strcmp(argv[4], "default")) { + char *prms[] = { argv[0], argv[1], "set_mac_default", + nvs_file }; + + return handle_cmd(state, II_NETDEV, ARRAY_SIZE(prms), prms); + } + + if (!strcmp(argv[4], "from_fuse")) { + char *prms[] = { argv[0], argv[1], "set_mac_from_fuse", + nvs_file }; + + return handle_cmd(state, II_NETDEV, ARRAY_SIZE(prms), prms); + } + + if (nvs_set_mac(nvs_file, argv[4]) != 0) + return 1; + + return 0; +} +COMMAND(plt, set_mac, " [|from_fuse|default]", + 0, 0, CIB_NETDEV, plt_set_mac, + "Set a MAC address to the NVS file.\n\n" + "\tspecific address to use (XX:XX:XX:XX:XX:XX)\n" + "from_fuse\ttry to read from the fuse ROM, if not available the command fails\n" + "default\t\twrite 00:00:00:00:00:00 to have the driver read from the fuse ROM,\n" + "\t\t\tfails if not available\n" + "00:00:00:00:00:00\tforce use of a zeroed MAC address (use with caution!)\n"); diff --git a/plt.h b/plt.h new file mode 100644 index 0000000..8b534a9 --- /dev/null +++ b/plt.h @@ -0,0 +1,412 @@ +#ifndef __PLT_H +#define __PLT_H + +#include + +#ifdef ANDROID +#define CURRENT_NVS_NAME "/system/etc/firmware/ti-connectivity/wl12xx-nvs.bin" +#define INSMOD_PATH "/system/bin/insmod" +#define RMMOD_PATH "/system/bin/rmmod" +#else +#define CURRENT_NVS_NAME "/lib/firmware/ti-connectivity/wl12xx-nvs.bin" +#define INSMOD_PATH "/sbin/insmod" +#define RMMOD_PATH "/sbin/rmmod" +#endif +#define NEW_NVS_NAME "./new-nvs.bin" +#define NVS_FILE_SIZE_127X 0x390 +#define NVS_FILE_SIZE_128X 0x459 + +/* NVS definition start here */ + +#define NVS_TX_TYPE_INDEX 0 + +#define START_TYPE_INDEX_IN_TLV 0 +#define TLV_TYPE_LENGTH 1 +#define START_LENGTH_INDEX \ +(START_TYPE_INDEX_IN_TLV + TLV_TYPE_LENGTH) /* 1 */ +#define TLV_LENGTH_LENGTH 2 +#define START_PARAM_INDEX \ +(START_LENGTH_INDEX + TLV_LENGTH_LENGTH) /* 3 */ + +#define NVS_MAC_FIRST_LENGTH_INDEX 0 +#define NVS_MAC_FIRST_LENGHT_VALUE 1 + +#define NVS_MAC_L_ADDRESS_INDEX \ +((NVS_MAC_FIRST_LENGTH_INDEX) + 1) /* 1*/ +#define NVS_MAC_L_ADDRESS_LENGTH 2 + +#define NVS_MAC_L_VALUE_INDEX \ +((NVS_MAC_L_ADDRESS_INDEX) + (NVS_MAC_L_ADDRESS_LENGTH)) /* 3 */ + +#define NVS_MAC_L_VALUE_LENGTH 4 + +#define NVS_MAC_SECONDE_LENGTH_INDEX \ +((NVS_MAC_L_VALUE_INDEX) + 4) /* 7 */ +#define NVS_MAC_SECONDE_LENGHT_VALUE 1 + +#define NVS_MAC_H_ADDRESS_INDEX \ +((NVS_MAC_SECONDE_LENGTH_INDEX) + 1) /* 8*/ +#define NVS_MAC_H_ADDRESS_LENGTH 2 + +#define NVS_MAC_H_VALUE_INDEX \ +((NVS_MAC_H_ADDRESS_INDEX) + (NVS_MAC_H_ADDRESS_LENGTH)) /* 10 */ +#define NVS_MAC_H_VALUE_LENGTH 4 + +#define NVS_END_BURST_TRANSACTION_INDEX \ +((NVS_MAC_H_VALUE_INDEX) + (NVS_MAC_H_VALUE_LENGTH)) /* 14 */ +#define NVS_END_BURST_TRANSACTION_VALUE 0 +#define NVS_END_BURST_TRANSACTION_LENGTH 7 + +#define NVS_ALING_TLV_START_ADDRESS_INDEX \ +((NVS_END_BURST_TRANSACTION_INDEX) + \ +(NVS_END_BURST_TRANSACTION_LENGTH)) /* 21 */ +#define NVS_ALING_TLV_START_ADDRESS_VALUE 0 +#define NVS_ALING_TLV_START_ADDRESS_LENGTH 3 + + +/* NVS pre TLV length */ +#define NVS_PRE_PARAMETERS_LENGTH \ +((NVS_ALING_TLV_START_ADDRESS_INDEX) + \ +(NVS_ALING_TLV_START_ADDRESS_LENGTH)) /* 24 */ + +/* NVS P2G table */ +#define NVS_TX_P2G_TABLE_LENGTH \ +((NUMBER_OF_SUB_BANDS_E) * 1 /* byte */) /* 8 */ + +/* NVS PPA table */ +#define NVS_TX_PPA_STEPS_TABLE_LENGTH \ +((NUMBER_OF_SUB_BANDS_E) * ((TXPWR_CFG0__VGA_STEP__NUMBER_OF_STEPS_E) \ +- 1) * 1 /* byte */) /* 32 */ + +/* NVS version 1 TX PD curve table length */ +#define NVS_TX_PD_TABLE_LENGTH_NVS_V1 (1 /* byte to set size of table */ + \ +((NUMBER_OF_SUB_BANDS_E) * (2 /* 1 byte offset, 1 byte low range */ + \ +2 /* first index in table */ + (((SIZE_OF_POWER_DETECTOR_TABLE) - 1) * \ +1 /* 1 byte */)))) /* 233 */ + +/* NVS version 2 TX PD curve table length */ +#define NVS_TX_PD_TABLE_LENGTH_NVS_V2 \ +((NUMBER_OF_SUB_BANDS_E) * (12 /* 12index of one byte -2 dBm - 9dBm */ +\ +28 /* 14 indexes of 2 byte -3dBm, 10dBm - 22 dBm */)) /* 320 */ + +/* NVS version 1 TX parameters Length */ +#define NVS_TX_PARAM_LENGTH_NVS_V1 \ +((NVS_TX_P2G_TABLE_LENGTH) + (NVS_TX_PPA_STEPS_TABLE_LENGTH) +\ +(NVS_TX_PD_TABLE_LENGTH_NVS_V1)) /* 273 */ + +/* NVS version 2 TX parameters Length */ +#define NVS_TX_PARAM_LENGTH_NVS_V2 \ +((NVS_TX_P2G_TABLE_LENGTH) + (NVS_TX_PPA_STEPS_TABLE_LENGTH) +\ +(NVS_TX_PD_TABLE_LENGTH_NVS_V2) +\ +(NUMBER_OF_RADIO_CHANNEL_INDEXS_E /* for Per Channel power Gain Offset tabl */)) + +/* NVS TX version */ +/* #define NVS_TX_PARAM_LENGTH NVS_TX_PARAM_LENGTH_NVS_V2 */ +#define NVS_TX_PARAM_LENGTH 0x199 + +/* NVS RX version */ +#define NVS_RX_PARAM_LENGTH NUMBER_OF_RX_BIP_EFUSE_PARAMETERS_E /* 19 */ + +/* NVS version parameter length */ +#define NVS_VERSION_PARAMETER_LENGTH 3 + +/* NVS max length */ +/* original ((NVS_TOTAL_LENGTH) + 4 - ((NVS_TOTAL_LENGTH) % 4)) */ +#define NVS_TOTAL_LENGTH 500 + +/* TLV max length */ +#define MAX_TLV_LENGTH NVS_TOTAL_LENGTH + +#define MAX_NVS_VERSION_LENGTH 12 + +enum wl1271_tm_commands { + WL1271_TM_CMD_UNSPEC, + WL1271_TM_CMD_TEST, + WL1271_TM_CMD_INTERROGATE, + WL1271_TM_CMD_CONFIGURE, + WL1271_TM_CMD_NVS_PUSH, + WL1271_TM_CMD_SET_PLT_MODE, + WL1271_TM_CMD_RECOVER, + WL1271_TM_CMD_GET_MAC, + + __WL1271_TM_CMD_AFTER_LAST +}; + +enum wl1271_tm_attrs { + WL1271_TM_ATTR_UNSPEC, + WL1271_TM_ATTR_CMD_ID, + WL1271_TM_ATTR_ANSWER, + WL1271_TM_ATTR_DATA, + WL1271_TM_ATTR_IE_ID, + WL1271_TM_ATTR_PLT_MODE, + __WL1271_TM_ATTR_AFTER_LAST +}; + +#define WL1271_TM_ATTR_MAX (__WL1271_TM_ATTR_AFTER_LAST - 1) + +enum wl1271_test_cmds { + TEST_CMD_PD_BUFFER_CAL = 0x1, /* TX PLT */ + TEST_CMD_P2G_CAL, /* TX BiP */ + TEST_CMD_RX_PLT_ENTER, + TEST_CMD_RX_PLT_CAL, /* RSSI Cal */ + TEST_CMD_RX_PLT_EXIT, + TEST_CMD_RX_PLT_GET, + TEST_CMD_FCC, /* Continuous TX */ + TEST_CMD_TELEC, /* Carrier wave in a specific channel and band */ + TEST_CMD_STOP_TX, /* Stop FCC or TELEC */ + TEST_CMD_PLT_TEMPLATE, /* define Template for TX */ + TEST_CMD_PLT_GAIN_ADJUST, + TEST_CMD_PLT_GAIN_GET, + TEST_CMD_CHANNEL_TUNE, + TEST_CMD_FREE_RUN_RSSI, /* Free running RSSI measurement */ + TEST_CMD_DEBUG, /* test command for debug using the struct: */ + TEST_CMD_CLPC_COMMANDS, + RESERVED_4, + TEST_CMD_RX_STAT_STOP, + TEST_CMD_RX_STAT_START, + TEST_CMD_RX_STAT_RESET, + TEST_CMD_RX_STAT_GET, + TEST_CMD_LOOPBACK_START, /* for FW Test Debug */ + TEST_CMD_LOOPBACK_STOP, /* for FW Test Debug */ + TEST_CMD_GET_FW_VERSIONS, + TEST_CMD_INI_FILE_RADIO_PARAM, + TEST_CMD_RUN_CALIBRATION_TYPE, + TEST_CMD_TX_GAIN_ADJUST, + TEST_CMD_UPDATE_PD_BUFFER_ERRORS, + TEST_CMD_UPDATE_PD_REFERENCE_POINT, + TEST_CMD_INI_FILE_GENERAL_PARAM, + TEST_CMD_SET_EFUSE, + TEST_CMD_GET_EFUSE, + TEST_CMD_TEST_TONE, + TEST_CMD_POWER_MODE, + TEST_CMD_SMART_REFLEX, + TEST_CMD_CHANNEL_RESPONSE, + TEST_CMD_DCO_ITRIM_FEATURE, + TEST_CMD_INI_FILE_RF_EXTENDED_PARAM, /* !!! Not exists in 128x */ + TEST_CMD_SET_NVS_VERSION, /* For wl128x, the value is minus 1 */ + MAX_TEST_CMD_ID = 0xFF +}; + +enum plt_mode { + PLT_OFF = 0, + PLT_ON = 1, + PLT_FEM_DETECT = 2, + PLT_CHIP_AWAKE = 3, +}; + +struct wl1271_cmd_header { + __u16 id; + __u16 status; + /* payload */ + unsigned char data[0]; +} __attribute__((packed)); + +struct wl1271_cmd_test_header { + unsigned char id; + unsigned char padding[3]; +} __attribute__((packed)); + +struct wl1271_cmd_cal_channel_tune { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + unsigned char band; + unsigned char channel; + + __le16 radio_status; +} __attribute__((packed)); + +struct wl1271_cmd_cal_update_ref_point { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le32 ref_power; + __le32 ref_detector; + unsigned char sub_band; + unsigned char padding[3]; +} __attribute__((packed)); + +struct wl1271_cmd_cal_tx_tone { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le16 radio_status; + unsigned char padding[2]; + __le32 power; + unsigned char tone_type; + unsigned char unused[11]; +} __attribute__((packed)); + +#define NVS_VERSION_2 2 +#define NVS_VERSION_2_1 21 + +struct wl1271_cmd_set_nvs_ver { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le16 radio_status; + + unsigned char nvs_ver; + unsigned char padding; +} __attribute__((packed)); + +struct wl1271_cmd_cal_p2g { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le32 ver; + __le16 len; + unsigned char buf[MAX_TLV_LENGTH]; + unsigned char type; + unsigned char padding; + + __le16 radio_status; + + unsigned char sub_band_mask; + unsigned char padding2; +} __attribute__((packed)); + +#define MAC_ADDR_LEN 6 + +struct wl1271_cmd_pkt_params { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + __le16 radio_status; + unsigned char padding[2]; + __le32 delay; + __le32 rate; + __le16 size; + __le16 amount; + __le32 power; + __le16 seed; + unsigned char pkt_mode; + unsigned char dcf_enable; + unsigned char g_interval; + unsigned char preamble; + unsigned char type; + unsigned char scramble; + unsigned char clpc_enable; + unsigned char seq_nbr_mode; + unsigned char src_mac[MAC_ADDR_LEN]; + unsigned char dst_mac[MAC_ADDR_LEN]; + unsigned char padding1[2]; +} __attribute__((packed)); + +struct wl1271_rx_path_statcs { + __le32 nbr_rx_valid_pkts; + __le32 nbr_rx_fcs_err_pkts; + __le32 nbr_rx_plcp_err_pkts; + __le32 seq_nbr_miss_cnt; /* For PER calculation */ + __le16 ave_snr; /* average SNR */ + __le16 ave_rssi; /* average RSSI */ + __le16 ave_evm; + unsigned char padding[2]; +} __attribute__((packed)); + +struct wl1271_rx_pkt_statcs { + __le32 length; + __le32 evm; + __le32 rssi; + __le16 freq_delta; + __le16 flags; + char type; + unsigned char rate; + unsigned char noise; + unsigned char agc_gain; + unsigned char padding[2]; +} __attribute__((packed)); + +#define RX_STAT_PACKETS_PER_MESSAGE (20) + +struct wl1271_radio_rx_statcs { + struct wl1271_cmd_header header; + + struct wl1271_cmd_test_header test; + + struct wl1271_rx_path_statcs rx_path_statcs; + __le32 base_pkt_id; + __le32 nbr_pkts; /* input/output: number of following packets */ + __le32 nbr_miss_pkts; + __le16 radio_status; + unsigned char padding[2]; +} __attribute__((packed)); + +struct fw_version { + int ver[5]; +} __attribute__((packed)); + +enum wl1271_nvs_type { + eNVS_VERSION = 0xaa, + eNVS_RADIO_TX_PARAMETERS = 1, + eNVS_RADIO_RX_PARAMETERS = 2, + eNVS_RADIO_INI = 16, + eNVS_NON_FILE = 0xFE, + eTLV_LAST = 0xFF /* last TLV type */ +}; + +#define DEFAULT_EFUSE_VALUE (0) + +enum wl1271_nvs_type_info { + eFIRST_RADIO_TYPE_PARAMETERS_INFO, + eNVS_RADIO_TX_TYPE_PARAMETERS_INFO = eFIRST_RADIO_TYPE_PARAMETERS_INFO, + eNVS_RADIO_RX_TYPE_PARAMETERS_INFO, + eLAST_RADIO_TYPE_PARAMETERS_INFO = eNVS_RADIO_RX_TYPE_PARAMETERS_INFO, + UNUSED_RADIO_TYPE_PARAMETERS_INFO, + eNUMBER_RADIO_TYPE_PARAMETERS_INFO = UNUSED_RADIO_TYPE_PARAMETERS_INFO, + LAST_RADIO_TYPE_PARAMETERS_INFO = + (eNUMBER_RADIO_TYPE_PARAMETERS_INFO - 1) +}; + +enum EFUSE_PARAMETER_TYPE_ENMT { + EFUSE_FIRST_PARAMETER_E, + /* RX PARAMETERS */ + EFUSE_FIRST_RX_PARAMETER_E = EFUSE_FIRST_PARAMETER_E, + RX_BIP_MAX_GAIN_ERROR_BAND_B_E = EFUSE_FIRST_RX_PARAMETER_E, + + RX_BIP_MAX_GAIN_ERROR_J_LOW_MID_E, + RX_BIP_MAX_GAIN_ERROR_J_HIGH_E, + + RX_BIP_MAX_GAIN_ERROR_5G_1ST_E, + RX_BIP_MAX_GAIN_ERROR_5G_2ND_E, + RX_BIP_MAX_GAIN_ERROR_5G_3RD_E, + RX_BIP_MAX_GAIN_ERROR_5G_4TH_E, + + RX_BIP_LNA_STEP_CORR_BAND_B_4TO3_E, + RX_BIP_LNA_STEP_CORR_BAND_B_3TO2_E, + RX_BIP_LNA_STEP_CORR_BAND_B_2TO1_E, + RX_BIP_LNA_STEP_CORR_BAND_B_1TO0_E, + + RX_BIP_LNA_STEP_CORR_BAND_A_4TO3_E, + RX_BIP_LNA_STEP_CORR_BAND_A_3TO2_E, + RX_BIP_LNA_STEP_CORR_BAND_A_2TO1_E, + RX_BIP_LNA_STEP_CORR_BAND_A_1TO0_E, + + RX_BIP_TA_STEP_CORR_BAND_B_2TO1_E, + RX_BIP_TA_STEP_CORR_BAND_B_1TO0_E, + RX_BIP_TA_STEP_CORR_BAND_A_2TO1_E, + RX_BIP_TA_STEP_CORR_BAND_A_1TO0_E, + NUMBER_OF_RX_BIP_EFUSE_PARAMETERS_E, + + /* TX PARAMETERS */ + TX_BIP_PD_BUFFER_GAIN_ERROR_E = NUMBER_OF_RX_BIP_EFUSE_PARAMETERS_E, + TX_BIP_PD_BUFFER_VBIAS_ERROR_E, + EFUSE_NUMBER_OF_PARAMETERS_E, + EFUSE_LAST_PARAMETER_E = (EFUSE_NUMBER_OF_PARAMETERS_E - 1) +} EFUSE_PARAMETER_TYPE_ENM; + +int get_mac_addr(int ifc_num, unsigned char *mac_addr); + +int file_exist(const char *filename); +int do_get_drv_info(char *dev_name, int *hw_ver, + struct ethtool_drvinfo *out_drvinfo); +int is_fw_ver_valid(char *dev_name, struct fw_version *fw_ver_valid); + + +#endif /* __PLT_H */ diff --git a/uim_rfkill/readme.txt b/uim_rfkill/readme.txt new file mode 100644 index 0000000..9d108a3 --- /dev/null +++ b/uim_rfkill/readme.txt @@ -0,0 +1,25 @@ +This is UIM utility required for TI's shared transport driver in order +to open UART and to deliver the control of it to the driver. + +The author of the utility is Pavan Savoy + +--- Running UIM utility + +It needs to be run at boot, Since linux flavors might require Bluetooth or +GPS to be turned on at boot. For this have the UIM entry in your either one +of your rc.S files or you can have special udev rule based on the platform +driver addition of device "kim". + +For Android, the following entry in init.rc should suffice, +service uim /system/bin/uim-sysfs + user root + group media bluetooth + oneshot +[edit] + +For Angstrom, the following command should be done, +./uim /dev/ttyO1 3686400 none 22 & + +--- Building UIM utility +make uim + diff --git a/uim_rfkill/uim.c b/uim_rfkill/uim.c new file mode 100644 index 0000000..9fd42bf --- /dev/null +++ b/uim_rfkill/uim.c @@ -0,0 +1,924 @@ +/* + * User Mode Init manager - For TI shared transport + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program;if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef ANDROID +#include +#include +#endif + +#ifdef ANDROID +#include /* for ppoll */ +#endif +#include "uim.h" + +#ifndef ANDROID +#define INCLUDE_FM 0 +#endif + +/* Maintains the exit state of UIM*/ +static int exiting; + +/* UART configuration parameters*/ +int uart_flow_control; +int cust_baud_rate; +char uart_dev_name[15]; +unsigned int uart_baud_rate; +struct termios ti; +int line_discipline; + +/* BD address as string and a pointer to array of hex bytes */ +char uim_bd_address[17]; +bdaddr_t *bd_addr; + +/* File descriptor for the UART device*/ +int dev_fd; + +/* Maintains the state of N_TI_WL line discipline installation*/ +unsigned char st_state = UNINSTALL_N_TI_WL; +unsigned char prev_st_state = UNINSTALL_N_TI_WL; + +/* from kernel's include/linux/rfkill.h + * the header in itself not included because of the + * version mismatch of android kernel headers project + */ + +/** + * enum rfkill_operation - operation types + * @RFKILL_OP_ADD: a device was added + * @RFKILL_OP_DEL: a device was removed + * @RFKILL_OP_CHANGE: a device's state changed -- userspace changes one device + * @RFKILL_OP_CHANGE_ALL: userspace changes all devices (of a type, or all) + */ +enum rfkill_operation { + RFKILL_OP_ADD = 0, + RFKILL_OP_DEL, + RFKILL_OP_CHANGE, + RFKILL_OP_CHANGE_ALL, +}; + +/** + * struct rfkill_event - events for userspace on /dev/rfkill + * @idx: index of dev rfkill + * @type: type of the rfkill struct + * @op: operation code + * @hard: hard state (0/1) + * @soft: soft state (0/1) + * + * Structure used for userspace communication on /dev/rfkill, + * used for events from the kernel and control to the kernel. + */ +#ifdef ANDROID +struct rfkill_event { + __u32 idx; + __u8 type; + __u8 op; + __u8 soft, hard; +} __packed; +#else +struct rfkill_event { + uint32_t idx; + uint8_t type; + uint8_t op; + uint8_t soft, hard; +} __packed; +#endif /* ANDROID */ + +/* to read events and filter notifications for us */ +struct rfkill_event rf_event; +unsigned int rfkill_idx; + +/*****************************************************************************/ +#ifdef UIM_DEBUG +/* Function to Read the firmware version + * module into the system. Currently used for + * debugging purpose, whenever the baud rate is changed + */ +void read_firmware_version() +{ + int index = 0; + char resp_buffer[20] = { 0 }; + unsigned char buffer[] = { 0x01, 0x01, 0x10, 0x00 }; + + UIM_START_FUNC(); + UIM_VER(" wrote %d bytes", (int)write(dev_fd, buffer, 4)); + UIM_VER(" reading %d bytes", (int)read(dev_fd, resp_buffer, 15)); + + for (index = 0; index < 15; index++) + UIM_VER(" %x ", resp_buffer[index]); + + printf("\n"); +} +#endif + +/*****************************************************************************/ +#ifdef ANDROID /* library for android to do insmod/rmmod */ + +/* Function to insert the kernel module into the system*/ +static int insmod(const char *filename, const char *args) +{ + void *module; + unsigned int size; + int ret = -1; + + UIM_START_FUNC(); + + module = (void *)load_file(filename, &size); + if (!module) + return ret; + + ret = init_module(module, size, args); + free(module); + + return ret; +} + +/* Function to remove the kernel module from the system*/ +static int rmmod(const char *modname) +{ + int ret = -1; + int maxtry = MAX_TRY; + + UIM_START_FUNC(); + + /* Retry MAX_TRY number of times in case of + * failure + */ + while (maxtry-- > 0) { + ret = delete_module(modname, O_NONBLOCK | O_EXCL); + if (ret < 0 && errno == EAGAIN) + sleep(1); + else + break; + } + + /* Failed to remove the module + */ + if (ret != 0) + UIM_ERR("Unable to unload driver module \"%s\": %s", + modname, strerror(errno)); + return ret; +} +#endif /*ANDROID*/ + +/*****************************************************************************/ +/* Function to read the HCI event from the given file descriptor + * + * This will parse the response received and returns error + * if the required response is not received + */ +int read_hci_event(int fd, unsigned char *buf, int size) +{ + int remain, rd; + int count = 0; + int reading = 1; + int rd_retry_count = 0; + struct timespec tm = {0, 50*1000*1000}; + + UIM_START_FUNC(); + + UIM_VER(" read_hci_event"); + if (size <= 0) + return -1; + + /* The first byte identifies the packet type. For HCI event packets, it + * should be 0x04, so we read until we get to the 0x04. */ + while (reading) { + rd = read(fd, buf, 1); + if (rd <= 0 && rd_retry_count++ < 4) { + nanosleep(&tm, NULL); + continue; + } else if (rd_retry_count >= 4) { + return -1; + } + + if (buf[0] == RESP_PREFIX) { + break; + } + } + count++; + + /* The next two bytes are the event code and parameter total length. */ + while (count < 3) { + rd = read(fd, buf + count, 3 - count); + if (rd <= 0) + return -1; + count += rd; + } + + /* Now we read the parameters. */ + if (buf[2] < (size - 3)) + remain = buf[2]; + else + remain = size - 3; + + while ((count - 3) < remain) { + rd = read(fd, buf + count, remain - (count - 3)); + if (rd <= 0) + return -1; + count += rd; + } + + return count; +} + +/* Function to read the Command complete event + * + * This will read the response for the change speed + * command that was sent to configure the UART speed + * with the custom baud rate + */ +static int read_command_complete(int fd, unsigned short opcode) +{ + command_complete_t resp; + + UIM_START_FUNC(); + + UIM_VER(" Command complete started"); + if (read_hci_event(fd, (unsigned char *)&resp, sizeof(resp)) < 0) { + UIM_ERR(" Invalid response"); + return -1; + } + + /* Response should be an event packet */ + if (resp.uart_prefix != HCI_EVENT_PKT) { + UIM_ERR + (" Error in response: not an event packet, but 0x%02x!", + resp.uart_prefix); + return -1; + } + + /* Response should be a command complete event */ + if (resp.hci_hdr.evt != EVT_CMD_COMPLETE) { + /* event must be event-complete */ + UIM_ERR + (" Error in response: not a cmd-complete event,but 0x%02x!", + resp.hci_hdr.evt); + return -1; + } + + if (resp.hci_hdr.plen < 4) { + /* plen >= 4 for EVT_CMD_COMPLETE */ + UIM_ERR(" Error in response: plen is not >= 4, but 0x%02x!", + resp.hci_hdr.plen); + return -1; + } + + if (resp.cmd_complete.opcode != (unsigned short)opcode) { + UIM_ERR(" Error in response: opcode is 0x%04x, not 0x%04x!", + resp.cmd_complete.opcode, opcode); + return -1; + } + + UIM_DBG(" Command complete done"); + return resp.status == 0 ? 0 : -1; +} + +/* Function to set the default baud rate + * + * The default baud rate of 115200 is set to the UART from the host side + * by making a call to this function.This function is also called before + * making a call to set the custom baud rate + */ +static int set_baud_rate() +{ + UIM_START_FUNC(); + + tcflush(dev_fd, TCIOFLUSH); + + /* Get the attributes of UART */ + if (tcgetattr(dev_fd, &ti) < 0) { + UIM_ERR(" Can't get port settings"); + return -1; + } + + /* Change the UART attributes before + * setting the default baud rate*/ + cfmakeraw(&ti); + + ti.c_cflag |= 1; + ti.c_cflag |= CRTSCTS; + + /* Set the attributes of UART after making + * the above changes + */ + tcsetattr(dev_fd, TCSANOW, &ti); + + /* Set the actual default baud rate */ + cfsetospeed(&ti, B115200); + cfsetispeed(&ti, B115200); + tcsetattr(dev_fd, TCSANOW, &ti); + + tcflush(dev_fd, TCIOFLUSH); + UIM_DBG(" set_baud_rate() done"); + + return 0; +} + +/* Function to set the UART custom baud rate. + * + * The UART baud rate has already been + * set to default value 115200 before calling this function. + * The baud rate is then changed to custom baud rate by this function*/ +static int set_custom_baud_rate() +{ + UIM_START_FUNC(); + + struct termios2 ti2; + + UIM_VER(" Changing baud rate to %u, flow control to %u", + cust_baud_rate, uart_flow_control); + + /* Flush non-transmitted output data, + * non-read input data or both*/ + tcflush(dev_fd, TCIOFLUSH); + + /*Set the UART flow control */ + if (uart_flow_control) + ti.c_cflag |= CRTSCTS; + else + ti.c_cflag &= ~CRTSCTS; + + /* + * Set the parameters associated with the UART + * The change will occur immediately by using TCSANOW + */ + if (tcsetattr(dev_fd, TCSANOW, &ti) < 0) { + UIM_ERR(" Can't set port settings"); + return -1; + } + + tcflush(dev_fd, TCIOFLUSH); + + /*Set the actual baud rate */ + ioctl(dev_fd, TCGETS2, &ti2); + ti2.c_cflag &= ~CBAUD; + ti2.c_cflag |= BOTHER; + ti2.c_ospeed = cust_baud_rate; + ioctl(dev_fd, TCSETS2, &ti2); + + UIM_DBG(" set_custom_baud_rate() done"); + return 0; +} + +/* + * Handling the Signals sent from the Kernel Init Manager. + * After receiving the signals, configure the baud rate, flow + * control and Install the N_TI_WL line discipline + */ +int st_sig_handler(int signo) +{ + int ldisc, len; + uim_speed_change_cmd cmd; + + uim_bdaddr_change_cmd addr_cmd; + + UIM_START_FUNC(); + + /* Raise a signal after when UIM is killed. + * This will exit UIM, and remove the inserted kernel + * modules + */ + if (signo == SIGINT) { + UIM_DBG(" Exiting. . ."); + exiting = 1; + return -1; + } + + /* Install the line discipline when the signal is received by UIM. + * Whenever the first protocol tries to register with the ST core, the + * ST KIM will send a signal SIGUSR2 to the UIM to install the N_TI_WL + * line discipline and do the host side UART configurations. + * + * On failure, ST KIM's line discipline installation times out, and the + * relevant protocol register fails + */ + if (st_state == INSTALL_N_TI_WL) { + UIM_VER(" signal received, opening %s", uart_dev_name); + dev_fd = open(uart_dev_name, O_RDWR); + if (dev_fd < 0) { + UIM_ERR(" Can't open %s", uart_dev_name); + return -1; + } + /* + * Set only the default baud rate. + * This will set the baud rate to default 115200 + */ + if (set_baud_rate() < 0) { + UIM_ERR(" set_baudrate() failed"); + close(dev_fd); + return -1; + } + + fcntl(dev_fd, F_SETFL,fcntl(dev_fd, F_GETFL) | O_NONBLOCK); + /* Set only thecustom baud rate */ + if (cust_baud_rate) { + + /* Forming the packet for Change speed command */ + cmd.uart_prefix = HCI_COMMAND_PKT; + cmd.hci_hdr.opcode = HCI_HDR_OPCODE; + cmd.hci_hdr.plen = sizeof(unsigned long); + cmd.speed = cust_baud_rate; + + /* Writing the change speed command to the UART + * This will change the UART speed at the controller + * side + */ + UIM_VER(" Setting speed to %d", cust_baud_rate); + len = write(dev_fd, &cmd, sizeof(cmd)); + if (len < 0) { + UIM_ERR(" Failed to write speed-set command"); + close(dev_fd); + return -1; + } + + /* Read the response for the Change speed command */ + if (read_command_complete(dev_fd, HCI_HDR_OPCODE) < 0) { + close(dev_fd); + return -1; + } + + UIM_VER(" Speed changed to %d", cust_baud_rate); + + /* Set the actual custom baud rate at the host side */ + if (set_custom_baud_rate() < 0) { + UIM_ERR(" set_custom_baud_rate() failed"); + close(dev_fd); + + return -1; + } + + /* Set the uim BD address */ + if (uim_bd_address[0] != 0) { + + memset(&addr_cmd, 0, sizeof(addr_cmd)); + /* Forming the packet for change BD address command*/ + addr_cmd.uart_prefix = HCI_COMMAND_PKT; + addr_cmd.hci_hdr.opcode = WRITE_BD_ADDR_OPCODE; + addr_cmd.hci_hdr.plen = sizeof(bdaddr_t); + memcpy(&addr_cmd.addr, bd_addr, sizeof(bdaddr_t)); + + /* Writing the change BD address command to the UART + * This will change the change BD address at the controller + * side + */ + len = write(dev_fd, &addr_cmd, sizeof(addr_cmd)); + if (len < 0) { + UIM_ERR(" Failed to write BD address command"); + close(dev_fd); + return -1; + } + + /* Read the response for the change BD address command */ + if (read_command_complete(dev_fd, WRITE_BD_ADDR_OPCODE) < 0) { + close(dev_fd); + return -1; + } + + UIM_VER(" BD address changed to %s", uim_bd_address); + } +#ifdef UIM_DEBUG + read_firmware_version(); +#endif + } + + /* After the UART speed has been changed, the IOCTL is + * is called to set the line discipline to N_TI_WL + */ + ldisc = line_discipline; + if (ioctl(dev_fd, TIOCSETD, &ldisc) < 0) { + UIM_ERR(" Can't set line discipline"); + close(dev_fd); + return -1; + } + + UIM_DBG(" Installed N_TI_WL Line displine"); + } + else { + UIM_DBG(" Un-Installed N_TI_WL Line displine"); + /* UNINSTALL_N_TI_WL - When the Signal is received from KIM */ + /* closing UART fd */ + close(dev_fd); + } + prev_st_state = st_state; + return 0; +} +int remove_modules() +{ + int err = 0; + +#ifdef ANDROID + UIM_VER(" Removing gps_drv "); + if (rmmod("gps_drv") != 0) { + UIM_ERR(" Error removing gps_drv module"); + err = -1; + } else { + UIM_DBG(" Removed gps_drv module"); + } + + UIM_VER(" Removing fm_drv "); + if (rmmod("fm_drv") != 0) { + UIM_ERR(" Error removing fm_drv module"); + err = -1; + } else { + UIM_DBG(" Removed fm_drv module"); + } + UIM_DBG(" Removed fm_drv module"); + + UIM_VER(" Removing bt_drv "); + + if (rmmod("bt_drv") != 0) { + UIM_ERR(" Error removing bt_drv module"); + err = -1; + } else { + UIM_DBG(" Removed bt_drv module"); + } + UIM_DBG(" Removed bt_drv module"); + + /*Remove the Shared Transport */ + UIM_VER(" Removing st_drv "); + + if (rmmod("st_drv") != 0) { + UIM_ERR(" Error removing st_drv module"); + err = -1; + } else { + UIM_DBG(" Removed st_drv module "); + } + UIM_DBG(" Removed st_drv module "); +#else +#if INCLUDE_FM + UIM_VER(" Removing fm_drv "); + if (system("rmmod fm_drv") != 0) { + UIM_ERR(" Error removing fm_drv module"); + err = -1; + } else { + UIM_DBG(" Removed fm_drv module"); + } +#endif /* INCLUDE_FM */ + UIM_VER(" Removing bt_drv "); + if (system("rmmod bt_drv") != 0) { + UIM_ERR(" Error removing bt_drv module"); + err = -1; + } else { + UIM_DBG(" Removed bt_drv module"); + } + + /*Remove the Shared Transport */ + UIM_VER(" Removing st_drv "); + + if (system("rmmod st_drv") != 0) { + UIM_ERR(" Error removing st_drv module"); + err = -1; + } else { + UIM_DBG(" Removed st_drv module "); + } +#endif + return err; +} + +int change_rfkill_perms(void) +{ + int fd, id, sz; + char path[64]; + char buf[16]; + for (id = 0; id < 50; id++) { + snprintf(path, sizeof(path), "/sys/class/rfkill/rfkill%d/type", id); + fd = open(path, O_RDONLY); + if (fd < 0) { + UIM_DBG("open(%s) failed: %s (%d)\n", path, strerror(errno), errno); + continue; + } + sz = read(fd, &buf, sizeof(buf)); + close(fd); + if (sz >= 9 && memcmp(buf, "bluetooth", 9) == 0) { + UIM_DBG("found bluetooth rfkill entry @ %d\n", id); + rfkill_idx = id; + break; + } + } + if (id == 50) { + return -1; + } +#ifdef ANDROID + sprintf(path, "/sys/class/rfkill/rfkill%d/state", id); + sz = chown(path, AID_BLUETOOTH, AID_BLUETOOTH); + if (sz < 0) { + UIM_ERR("change mode failed for %s (%d)\n", path, errno); + return -1; + } +#endif /* ANDROID */ + /* + * bluetooth group's user system needs write permission + */ + sz = chmod(path, 0660); + if (sz < 0) { + UIM_ERR("change mode failed for %s (%d)\n", path, errno); + return -1; + } + UIM_DBG("changed permissions for %s(%d) \n", path, sz); + /* end of change_perms */ + + return 0; +} + +void *bt_malloc(size_t size) +{ + return malloc(size); +} + +/* Function to convert the BD address from ascii to hex value */ +bdaddr_t *strtoba(const char *str) +{ + const char *ptr = str; + int i; + + uint8_t *ba = bt_malloc(sizeof(bdaddr_t)); + if (!ba) + return NULL; + + for (i = 0; i < 6; i++) { + ba[i] = (uint8_t) strtol(ptr, NULL, 16); + if (i != 5 && !(ptr = strchr(ptr, ':'))) + ptr = ":00:00:00:00:00"; + ptr++; + } + + return (bdaddr_t *) ba; +} + +/*****************************************************************************/ +int main(int argc, char *argv[]) +{ + int st_fd,err; + struct stat file_stat; +#ifndef ANDROID /* used on ubuntu */ + char *tist_ko_path; + struct utsname name; +#endif + struct pollfd p; + sigset_t sigs; + + UIM_START_FUNC(); + err = 0; + + /* Parse the user input */ + if ((argc == 5) || (argc == 6)) { + strcpy(uart_dev_name, argv[1]); + uart_baud_rate = atoi(argv[2]); + uart_flow_control = atoi(argv[3]); + line_discipline = atoi(argv[4]); + + /* Depending upon the baud rate value, differentiate + * the custom baud rate and default baud rate + */ + switch (uart_baud_rate) { + case 115200: + UIM_VER(" Baudrate 115200"); + break; + case 9600: + case 19200: + case 38400: + case 57600: + case 230400: + case 460800: + case 500000: + case 576000: + case 921600: + case 1000000: + case 1152000: + case 1500000: + case 2000000: + case 2500000: + case 3000000: + case 3500000: + case 3686400: + case 4000000: + cust_baud_rate = uart_baud_rate; + UIM_VER(" Baudrate %d", cust_baud_rate); + break; + default: + UIM_ERR(" Inavalid Baud Rate"); + break; + } + + memset(&uim_bd_address, 0, sizeof(uim_bd_address)); + } else { + UIM_ERR(" Invalid arguements"); + UIM_ERR(" Usage: uim [Uart device] [Baud rate] [Flow control] [Line discipline] "); + return -1; + } + if (argc == 6) { + /* BD address passed as string in xx:xx:xx:xx:xx:xx format */ + strcpy(uim_bd_address, argv[5]); + bd_addr = strtoba(uim_bd_address); + } + + +#ifndef ANDROID + if (uname (&name) == -1) { + UIM_ERR("cannot get kernel release name"); + return -1; + } +#else /* if ANDROID */ + + if (0 == lstat("/st_drv.ko", &file_stat)) { + if (insmod("/st_drv.ko", "") < 0) { + UIM_ERR(" Error inserting st_drv module"); + return -1; + } else { + UIM_DBG(" Inserted st_drv module"); + } + } else { + if (0 == lstat("/dev/rfkill", &file_stat)) { + UIM_DBG("ST built into the kernel ?"); + } else { + UIM_ERR("BT/FM/GPS would be unavailable on system"); + UIM_ERR(" rfkill device '/dev/rfkill' not found "); + return -1; + } + } +#endif + +#ifndef ANDROID + /*-- Insmod of ST driver --*/ + asprintf(&tist_ko_path, + "/lib/modules/%s/kernel/drivers/misc/ti-st/st_drv.ko",name.release); + if (0 == lstat(tist_ko_path, &file_stat)) { + if (system("insmod /lib/modules/`uname -r`/kernel/drivers/misc/ti-st/st_drv.ko") != 0) { + UIM_ERR(" Error inserting st_drv module"); + free(tist_ko_path); + return -1; + } else { + UIM_DBG(" Inserted st_drv module"); + } + } else { + UIM_ERR("ST driver built into the kernel ?"); + } + free(tist_ko_path); +#endif + + if (change_rfkill_perms() < 0) { + /* possible error condition */ + UIM_ERR("rfkill not enabled in st_drv - BT on from UI might fail\n"); + } + +#ifndef ANDROID + /*-- Insmod of BT driver --*/ + asprintf(&tist_ko_path, + "/lib/modules/%s/kernel/drivers/staging/ti-st/bt_drv.ko",name.release); + if (0 == lstat(tist_ko_path, &file_stat)) { + if (system("insmod /lib/modules/`uname -r`/kernel/drivers/staging/ti-st/bt_drv.ko") != 0) { + UIM_ERR(" Error inserting bt_drv module"); + system("rmmod st_drv"); + free(tist_ko_path); + return -1; + } else { + UIM_DBG(" Inserted bt_drv module"); + } + } else { + UIM_ERR("BT driver built into the kernel ?"); + } + free(tist_ko_path); + +#if INCLUDE_FM + /*-- Insmod of FM driver --*/ + asprintf(&tist_ko_path, + "/lib/modules/%s/kernel/drivers/staging/ti-st/fm_drv.ko",name.release); + if (0 == lstat(tist_ko_path, &file_stat)) { + if (system("insmod /lib/modules/`uname -r`/kernel/drivers/staging/ti-st/fm_drv.ko") != 0) { + UIM_ERR(" Error inserting fm_drv module"); + system("rmmod bt_drv"); + system("rmmod st_drv"); + free(tist_ko_path); + return -1; + } else { + UIM_DBG(" Inserted fm_drv module"); + } + } else { + UIM_ERR("FM driver built into the kernel ?"); + } + free(tist_ko_path); +#endif /* INCLUDE_FM */ +#else /* if ANDROID */ + if (0 == lstat("/bt_drv.ko", &file_stat)) { + if (insmod("/bt_drv.ko", "") < 0) { + UIM_ERR(" Error inserting bt_drv module, NO BT? "); + } else { + UIM_DBG(" Inserted bt_drv module"); + } + } else { + UIM_DBG("BT driver module un-available... "); + UIM_DBG("BT driver built into the kernel ?"); + } + + if (0 == lstat("/fm_drv.ko", &file_stat)) { + if (insmod("/fm_drv.ko", "") < 0) { + UIM_ERR(" Error inserting fm_drv module, NO FM? "); + } else { + UIM_DBG(" Inserted fm_drv module"); + } + } else { + UIM_DBG("FM driver module un-available... "); + UIM_DBG("FM driver built into the kernel ?"); + } + + if (0 == lstat("/gps_drv.ko", &file_stat)) { + if (insmod("/gps_drv.ko", "") < 0) { + UIM_ERR(" Error inserting gps_drv module, NO GPS? "); + } else { + UIM_DBG(" Inserted gps_drv module"); + } + } else { + UIM_DBG("GPS driver module un-available... "); + UIM_DBG("GPS driver built into the kernel ?"); + } + + if (chmod("/dev/tifm", 0666) < 0) { + UIM_ERR("unable to chmod /dev/tifm"); + } +#endif + /* rfkill device's open/poll/read */ + st_fd = open("/dev/rfkill", O_RDONLY); + if (st_fd < 0) { + UIM_DBG("unable to open /dev/rfkill (%s)", strerror(errno)); + remove_modules(); + return -1; + } + + + p.fd = st_fd; + p.events = POLLERR | POLLHUP | POLLOUT | POLLIN; + + sigfillset(&sigs); + sigdelset(&sigs, SIGCHLD); + sigdelset(&sigs, SIGPIPE); + sigdelset(&sigs, SIGTERM); + sigdelset(&sigs, SIGINT); + sigdelset(&sigs, SIGHUP); + +RE_POLL: + while (!exiting) { + p.revents = 0; +#ifdef ANDROID + err = ppoll(&p, 1, NULL, &sigs); +#else + err = poll(&p, 1, -1); +#endif /* ANDROID */ + if (err < 0 && errno == EINTR) + continue; + if (err) + break; + } + if (!exiting) + { + err = read(st_fd, &rf_event, sizeof(rf_event)); + UIM_DBG("rf_event: %d, %d, %d, %d, %d\n", rf_event.idx, + rf_event.type, rf_event.op ,rf_event.hard, + rf_event.soft); + if ((rf_event.op == RFKILL_OP_CHANGE) && + (rf_event.idx == rfkill_idx)) { + if (rf_event.hard == 1) /* hard blocked */ + st_state = UNINSTALL_N_TI_WL; + else /* unblocked */ + st_state = INSTALL_N_TI_WL; + + if (prev_st_state != st_state) + st_sig_handler(SIGUSR2); + } + goto RE_POLL; + } + + if(remove_modules() < 0) { + UIM_ERR(" Error removing modules "); + close(st_fd); + return -1; + } + + close(st_fd); + return 0; +} diff --git a/uim_rfkill/uim.h b/uim_rfkill/uim.h new file mode 100644 index 0000000..8797db1 --- /dev/null +++ b/uim_rfkill/uim.h @@ -0,0 +1,154 @@ +/* + * User Mode Init manager - For shared transport + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program;if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef UIM_H +#define UIM_H + +/* Paramaters to set the baud rate*/ +#define FLOW_CTL 0x0001 +#define BOTHER 0x00001000 +#define ARM_NCCS 19 + +#define TCGETS2 _IOR('T',0x2A, struct termios2) +#define TCSETS2 _IOW('T',0x2B, struct termios2) + +/*HCI Command and Event information*/ +#define HCI_HDR_OPCODE 0xff36 +#define WRITE_BD_ADDR_OPCODE 0xFC06 +#define RESP_PREFIX 0x04 +#define MAX_TRY 10 + +/* HCI Packet types */ +#define HCI_COMMAND_PKT 0x01 +#define HCI_EVENT_PKT 0x04 + +/* HCI command macros*/ +#define HCI_EVENT_HDR_SIZE 2 +#define HCI_COMMAND_HDR_SIZE 3 +#define UIM_WRITE_BD_ADDR_CP_SIZE 6 + + +/* HCI event macros*/ +#define EVT_CMD_COMPLETE_SIZE 3 +#define EVT_CMD_STATUS_SIZE 4 +#define EVT_CMD_COMPLETE 0x0E +#define EVT_CMD_STATUS 0x0F + + +#define VERBOSE +#ifndef ANDROID +#define LOGE printf +#endif /* ANDROID */ +#define LOG_TAG "uim-rfkill: " +#define UIM_ERR(fmt, arg...) LOGE("uim:"fmt"\n" , ##arg) +#if defined(UIM_DEBUG) /* limited debug messages */ +#define UIM_START_FUNC() LOGE("uim: Inside %s\n", __FUNCTION__) +#define UIM_DBG(fmt, arg...) LOGE("uim:"fmt"\n" , ## arg) +#define UIM_VER(fmt, arg...) +#elif defined(VERBOSE) /* very verbose */ +#define UIM_START_FUNC() LOGE("uim: Inside %s\n", __FUNCTION__) +#define UIM_DBG(fmt, arg...) LOGE("uim:"fmt"\n" , ## arg) +#define UIM_VER(fmt, arg...) LOGE("uim:"fmt"\n" , ## arg) +#else /* error msgs only */ +#define UIM_START_FUNC() +#define UIM_DBG(fmt, arg...) +#define UIM_VER(fmt, arg...) +#endif + +/*Termios2 structure for setting the Custom baud rate*/ +struct termios2 { + tcflag_t c_iflag; /* input mode flags */ + tcflag_t c_oflag; /* output mode flags */ + tcflag_t c_cflag; /* control mode flags */ + tcflag_t c_lflag; /* local mode flags */ + cc_t c_line; /* line discipline */ + cc_t c_cc[ARM_NCCS]; /* control characters */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ +}; + +/* HCI command header*/ +typedef struct { + uint16_t opcode; /* OCF & OGF */ + uint8_t plen; +} __attribute__ ((packed)) hci_command_hdr; + +/* HCI event header*/ +typedef struct { + uint8_t evt; + uint8_t plen; +} __attribute__ ((packed)) hci_event_hdr; + +/* HCI command complete event*/ +typedef struct { + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_complete; + +/* HCI event status*/ +typedef struct { + uint8_t status; + uint8_t ncmd; + uint16_t opcode; +} __attribute__ ((packed)) evt_cmd_status; + +/* HCI Event structure to set the cusrom baud rate*/ +typedef struct { + uint8_t uart_prefix; + hci_event_hdr hci_hdr; + evt_cmd_complete cmd_complete; + uint8_t status; + uint8_t data[16]; +} __attribute__ ((packed)) command_complete_t; + +/* HCI Command structure to set the cusrom baud rate*/ +typedef struct { + uint8_t uart_prefix; + hci_command_hdr hci_hdr; + uint32_t speed; +} __attribute__ ((packed)) uim_speed_change_cmd; + +/* BD address structure to set the uim BD address*/ +typedef struct { + unsigned char b[6]; +} __attribute__((packed)) bdaddr_t; + +/* HCI Command structure to set the uim BD address*/ +typedef struct { + uint8_t uart_prefix; + hci_command_hdr hci_hdr; + bdaddr_t addr; +} __attribute__ ((packed)) uim_bdaddr_change_cmd; + +/* Signal received from KIM will install line discipline at first, + * the next signal received from KIM will un-install the + * line discipline*/ +enum { + /* expecting signal from KIM to setup uart fd for ST */ + INSTALL_N_TI_WL, + + /* expecting signal from KIM to close uart fd */ + UNINSTALL_N_TI_WL, +}; + +/* Functions to insert and remove the kernel modules from the system*/ +extern int init_module(void *, unsigned int, const char *); +extern int delete_module(const char *, unsigned int); +extern int load_file(const char *, unsigned int *); + +#endif /* UIM_H */ diff --git a/wl18xx_plt.c b/wl18xx_plt.c new file mode 100644 index 0000000..6bfaf2a --- /dev/null +++ b/wl18xx_plt.c @@ -0,0 +1,787 @@ +/* + * This file is part of calibrator + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "calibrator.h" +#include "plt.h" +#include "wl18xx_plt.h" + +SECTION(wl18xx_plt); + +static int plt_wl18xx_set_antenna_diversity_5G(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_set_antenna_diversity_5G prms; + + if (argc != 1) + return 1; + + prms.test.id = WL18XX_TEST_CMD_5GHZ_ANTENNA_DIVERSITY; + prms.mode = (__u8)atoi(argv[0]); + + if (prms.mode > 1) + return 1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + printf("Calibrator:: Set antenna diversity 5G (mode=%d)\n", + prms.mode); + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, set_antenna_diversity_5G , "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_set_antenna_diversity_5G, + " Set antenna diversity 5G\n"); + + +static int plt_wl18xx_tx_tone_stop(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_phy_tx_tone_stop prms; + + if (argc != 0) + return 1; + + prms.test.id = WL18XX_TEST_CMD_STOP_TX_TONE; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + printf("Calibrator:: Stopping TX Tone\n"); + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, tx_tone_stop , "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_tx_tone_stop, + " Stop TX Tone\n"); + + + +static int plt_wl18xx_tx_tone_start(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_phy_tx_tone_start prms; + + if (argc != 4) + return 1; + + prms.test.id = WL18XX_TEST_CMD_START_TX_TONE; + + prms.mode = (__u8)atoi(argv[0]); + prms.bin_index = (__s8)atoi(argv[1]); + prms.trigger_iqram_recording = 0; + prms.sig_gen_cw_en = 0; + prms.sig_gen_mod_en = 0; + prms.ant_mode = (__u8)atoi(argv[2]); + prms.set_rx_aux_on = 0; + prms.gain_index = (__u8)atoi(argv[3]); + + if (prms.mode > 2) + return 1; + + if ((prms.bin_index > 32) || (prms.bin_index < (-32))) + return 1; + + if (prms.ant_mode > 2) + return 1; + + if (prms.gain_index > 4) + return 1; + + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + printf("Calibrator:: Starting TX Tone (mode=%d, bin_index=%d, ant_mode=%d, gain_index=%d)\n", + prms.mode, prms.bin_index, prms.ant_mode, prms.gain_index); + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, tx_tone_start , " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_tx_tone_start, + "Start TX Tone\n\n" + "\t\t tone mode:\n" + "\t\t\t0 = silence\n" + "\t\t\t1 = carrier feedthrough\n" + "\t\t\t2 = single tone\n" + "\t the offset (in round number of bins) of the tone from the carrier: [(-32)-32]\n" + "\t antenna selection:\n" + "\t\t\t0 = auto\n" + "\t\t\t1 = TX1\n" + "\t\t\t2 = TX2\n" + "\t PA gain step: 2.4GHz: 0-1, 5GHz: 0-4\n"); + + +static int plt_wl18xx_phy_reg_write(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_phy_reg_write prms; + + if (argc != 2) + return 1; + + prms.test.id = WL18XX_TEST_CMD_PHY_ADDR_WRITE; + + + prms.addr = strtol(argv[0], NULL, 16); + prms.data = strtol(argv[1], NULL, 16); + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, phy_reg_write , " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_phy_reg_write, + " Write PHY register for PLT.\n"); + + +static int plt_wl18xx_display_phy_reg_read(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + struct wl18xx_cmd_phy_reg_read *prms; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + prms = (struct wl18xx_cmd_phy_reg_read *) nla_data(td[WL1271_TM_ATTR_DATA]); + + printf("Register Address: \t0x%x\t", prms->addr); + printf("is:\t0x%x\n", prms->data); + + return NL_SKIP; +} + + +static int plt_wl18xx_phy_reg_read(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_phy_reg_read prms; + + if (argc != 1) + return 1; + + prms.test.id = WL18XX_TEST_CMD_PHY_ADDR_READ; + + prms.addr = strtol(argv[0], NULL, 16); + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + NLA_PUT_U8(msg, WL1271_TM_ATTR_ANSWER, 1); + + nla_nest_end(msg, key); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + plt_wl18xx_display_phy_reg_read, NULL); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, phy_reg_read , "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_phy_reg_read, + " Read PHY register for PLT.\n"); + + +static int plt_wl18xx_set_antenna_mode_5G(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_set_antenna_mode_5G prms; + + if (argc != 4) + return 1; + + prms.test.id = WL18XX_TEST_CMD_SET_ANTENNA_MODE_5G; + + + prms.mac_prim_rx_chain = (__u8)atoi(argv[0]); + prms.mac_rx_chain1_en = (__u8)atoi(argv[1]); + prms.mac_rx_chain2_en = (__u8)atoi(argv[2]); + prms.mac_tx_chain1_en = (__u8)atoi(argv[3]); + + if ((prms.mac_prim_rx_chain != 1 && prms.mac_prim_rx_chain != 2 )) + return 1; + + if ((prms.mac_rx_chain1_en > 1) || (prms.mac_rx_chain2_en > 1) + || (prms.mac_tx_chain1_en > 1)) + return 1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, set_antenna_mode_5G , " " + " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_set_antenna_mode_5G, + "set antenna mode 5G for PLT.\n"); + + +static int plt_wl18xx_set_antenna_mode_24G(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_set_antenna_mode_24G prms; + + if (argc != 6) + return 1; + + prms.test.id = WL18XX_TEST_CMD_SET_ANTENNA_MODE_24G; + + + prms.mac_prim_rx_chain = (__u8)atoi(argv[0]); + prms.mac_prim_tx_chain = (__u8)atoi(argv[1]); + prms.mac_rx_chain1_en = (__u8)atoi(argv[2]); + prms.mac_rx_chain2_en = (__u8)atoi(argv[3]); + prms.mac_tx_chain1_en = (__u8)atoi(argv[4]); + prms.mac_tx_chain2_en = (__u8)atoi(argv[5]); + + if ((prms.mac_prim_rx_chain != 1 && prms.mac_prim_rx_chain != 2 ) || + (prms.mac_prim_tx_chain != 1 && prms.mac_prim_tx_chain != 2 )) + return 1; + + if ((prms.mac_rx_chain1_en > 1) || (prms.mac_rx_chain2_en > 1) || + (prms.mac_tx_chain1_en > 1) || (prms.mac_tx_chain2_en > 1)) + return 1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, set_antenna_mode_24G , " " + " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_set_antenna_mode_24G, + "set antenna mode 2.4G for PLT.\n"); + +static int plt_wl18xx_set_tx_power(struct nl80211_state *state, + struct nl_cb *cb, struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_set_tx_power prms; + + if (argc != 12) + return 1; + + prms.test.id = WL18XX_TEST_CMD_TX_POWER; + + prms.mac_des_pwr = (__s32)atoi(argv[0]); + prms.mac_lvl_idx = atoi(argv[1]); + prms.freq_band = atoi(argv[2]); + prms.freq_prim_chan_num = atoi(argv[3]); + prms.freq_prim_chan_loc = atoi(argv[4]); + prms.mac_ant_select = atoi(argv[5]); + prms.mac_non_srv = atoi(argv[6]); + prms.mac_chan_lim_dis = atoi(argv[7]); + prms.mac_fem_lim_dis = atoi(argv[8]); + prms.mac_gain_calc_mode = atoi(argv[9]); + prms.mac_analog_gain_control_idx = atoi(argv[10]); + prms.mac_post_dpd_gain = atoi(argv[11]); + + if ((prms.mac_gain_calc_mode == 0) || (prms.mac_gain_calc_mode == 1)) { + if ((prms.mac_des_pwr > 20000) || (prms.mac_des_pwr < 0)) { + fprintf(stderr, "Power Mode: TX Output Power is out of range (0-20.000dBm)\n"); + return 1; + } + } + + if (prms.mac_gain_calc_mode == 2) { + if ((prms.mac_des_pwr > -4000) || (prms.mac_des_pwr < -15000)) { + fprintf(stderr, "dBPsat Mode: TX Output Power is out of range (-15.000-(-4.000)dBm)\n"); + return 1; + } + } + + if (prms.mac_lvl_idx > 3) + return 1; + + if (prms.freq_band > 2) + return 1; + + if (prms.freq_prim_chan_num <= 0) + return 1; + + if (prms.mac_ant_select > 3) + return 1; + + if (prms.mac_non_srv > 1) + return 1; + + if (prms.mac_chan_lim_dis > 1) + return 1; + + if (prms.mac_fem_lim_dis > 1) + return 1; + + if (prms.mac_gain_calc_mode > 2) + return 1; + + if (prms.mac_analog_gain_control_idx > 4) + return 1; + + if (prms.mac_post_dpd_gain > 4) + return 1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, set_tx_power, " " + " " + " " + " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_set_tx_power, + "set TX transmissions power for PLT.\n"); + +static int plt_wl18xx_tune_channel(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_channel_tune prms; + + if (argc != 3) + return 1; + + prms.test.id = WL18XX_TEST_CMD_CHANNEL_TUNE; + prms.channel = (__u8)atoi(argv[0]); + prms.band = (__u8)atoi(argv[1]); + prms.bandwidth = (__u8)atoi(argv[2]); + prms.rfSetIdx = 0; /* Set to Zero to support backward competability only for RTTT */ + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, tune_channel, " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_tune_channel, + "Set channel, band and bandwidth for PLT.\n"); + +#define RX_FILTER_MAGIC_NUMBER 0xabadabad + +static int plt_wl18xx_start_rx(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_start_rx prms; + + if (argc > 2) + return 1; + + prms.test.id = WL18XX_TEST_CMD_START_RX_SIMULATION; + + if (argc == 2) { + str2mac(prms.src_addr, argv[0]); + str2mac(prms.dst_addr, argv[1]); + prms.magic_num = RX_FILTER_MAGIC_NUMBER; + } + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + printf("Calibrator:: Starting RX Simulation (Note that statistics counters are being reset)...\n"); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, start_rx, " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_start_rx, + "Start gathering RX statistics for PLT.\n"); + +static int plt_wl18xx_stop_rx(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_stop_rx prms; + + if (argc != 0) + return 1; + + prms.test.id = WL18XX_TEST_CMD_STOP_RX_SIMULATION; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + printf("Calibrator:: Stopping RX Simulation\n"); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, stop_rx, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_stop_rx, + "Stop gathering RX statistics for PLT.\n"); + +static int plt_wl18xx_display_rx_stats(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *td[WL1271_TM_ATTR_MAX + 1]; + struct wl18xx_cmd_rx_stats *prms; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_TESTDATA]) { + fprintf(stderr, "no data!\n"); + return NL_SKIP; + } + + nla_parse(td, WL1271_TM_ATTR_MAX, nla_data(tb[NL80211_ATTR_TESTDATA]), + nla_len(tb[NL80211_ATTR_TESTDATA]), NULL); + + prms = (struct wl18xx_cmd_rx_stats *) nla_data(td[WL1271_TM_ATTR_DATA]); + + printf("\nRX statistics (status %d)\n", prms->radio_status); + printf("Total Received Packets:\t%d\n", prms->total); + printf("FCS Errors:\t\t%d\n", prms->errors); + printf("MAC Mismatch:\t\t%d\n", prms->addr_mm); + printf("Good Packets:\t\t%d\n", prms->good); + printf("Average RSSI (SOC):\t%d\n", prms->rssi_soc); + printf("Average RSSI (ANT):\t%d\n", prms->rssi_ant); + + if(prms->total) { + float per = ((float)prms->total - (float)prms->good)/(float)prms->total; + printf("PER:\t\t\t%f # PER = Total Bad / Total Received\n", per); + } else { + printf("PER:\t\t\tN/A # PER = Total Bad / Total Received\n"); + } + + return NL_SKIP; +} + +static int plt_wl18xx_get_rx_stats(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_rx_stats prms; + + if (argc != 0) + return 1; + + prms.test.id = WL18XX_TEST_CMD_GET_RX_STATISTICS; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + NLA_PUT_U8(msg, WL1271_TM_ATTR_ANSWER, 1); + + nla_nest_end(msg, key); + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + plt_wl18xx_display_rx_stats, NULL); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, get_rx_stats, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_get_rx_stats, + "Retrieve RX statistics for PLT.\n"); + +static int plt_wl18xx_start_tx(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_start_tx prms; + + if (argc != 11) + return 1; + + prms.test.id = WL18XX_TEST_CMD_START_TX_SIMULATION; + + prms.delay = atoi(argv[0]); + prms.rate = atoi(argv[1]); + prms.size = atoi(argv[2]); + prms.mode = atoi(argv[3]); + prms.data_type = atoi(argv[4]); + prms.gi = atoi(argv[5]); + prms.options1 = atoi(argv[6]); + prms.options2 = atoi(argv[7]); + str2mac(prms.src_addr, argv[8]); + str2mac(prms.dst_addr, argv[9]); + prms.bandwidth = atoi(argv[10]); + + if (prms.delay < 200) { + fprintf(stderr, "Delay is out of range (valid range >=200us)\n"); + return 1; + } + if (prms.size > 4065) { + fprintf(stderr, "Packet Size is out of range (valid range <=4065B)\n"); + return 1; + } + if (prms.rate < 0 || prms.rate > 29) + return 1; + if (prms.gi != 0 && prms.gi != 1) + return 1; + if (prms.bandwidth != 0 && prms.bandwidth != 1) + return 1; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + printf("Calibrator:: Starting TX Simulation (delay=%d, rate=%d, size=%d, mode=%d\n" + " data_type=%d, BW=%d, gi=%d, opt1=0x%x, opt2=0x%x\n" + " src=%02x:%02x:%02x:%02x:%02x:%02x\n" + " dst=%02x:%02x:%02x:%02x:%02x:%02x)...\n", + prms.delay, prms.rate, prms.size, prms.mode, prms.data_type, + prms.bandwidth, prms.gi, prms.options1,prms.options2, + prms.src_addr[0],prms.src_addr[1],prms.src_addr[2],prms.src_addr[3],prms.src_addr[4],prms.src_addr[5], + prms.dst_addr[0],prms.dst_addr[1],prms.dst_addr[2],prms.dst_addr[3],prms.dst_addr[4],prms.dst_addr[5]); + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, start_tx, " " + " ", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_start_tx, + "Start TX transmissions for PLT.\n\n" + "\t\tdelay between packets in us: 200-...\n" + "\t\ttransmission rate:\n" + "\t\t\t0 = 1.0 Mbps\t\t\t1 = 2.0 Mbps\n" + "\t\t\t2 = 5.0 Mbps\t\t\t3 = 11.0 Mbps\n" + "\t\t\t4 = 6.0 Mbps\t\t\t5 = 9.0 Mbps\n" + "\t\t\t6 = 12.0 Mbps\t\t\t7 = 18.0 Mbps\n" + "\t\t\t8 = 24.0 Mbps\t\t\t9 = 36.0 Mbps\n" + "\t\t\t10 = 48.0 Mbps\t\t\t11 = 54.0 Mbps\n" + "\t\t\t12 = 6.5 Mbps (MCS0)\t\t13 = 13.0 Mbps (MCS1)\n" + "\t\t\t14 = 19.5 Mbps (MCS2)\t\t15 = 26.0 Mbps (MCS3)\n" + "\t\t\t16 = 39.0 Mbps (MCS4)\t\t17 = 52.0 Mbps (MCS5)\n" + "\t\t\t18 = 58.5 Mbps (MCS6)\t\t19 = 65.0 Mbps (MCS7)\n" + "\t\t\t20 = MCS8/MCS4 at 40MHz\t21 = MCS9/MCS5 at 40MHz\n" + "\t\t\t22 = MCS10/MCS6 at 40MHz\t23 = MCS11/MCS7 at 40MHz\n" + "\t\t\t24 = MCS12/MCS7 at 40MHz SGI\t25 = MCS13\n" + "\t\t\t26 = MCS14\t\t\t27 = MCS15\n" + "\t\tpacket size (bytes): 0-4065\n" + "\t\tnumber of packets (0 = endless)\n" + "\tnot supported\n" + "\t\tguard interval (0 = normal, 1 = short)\n" + "\tunused\n" + "\tsupported options according to bitmap:\n" + "\t\t\tbit1 - override CCA\n" + "\t\t\tbit2 - fixed/incremental sequence number\n" + "\tsource MAC address (XX:XX:XX:XX:XX:XX)\n" + "\tdestination MAC address (XX:XX:XX:XX:XX:XX)\n" + "\tchannel width (0 = 20 MHz, 1 = 40 MHz)"); + +static int plt_wl18xx_stop_tx(struct nl80211_state *state, struct nl_cb *cb, + struct nl_msg *msg, int argc, char **argv) +{ + struct nlattr *key; + struct wl18xx_cmd_stop_tx prms; + + if (argc != 0) + return 1; + + prms.test.id = WL18XX_TEST_CMD_STOP_TX_SIMULATION; + + key = nla_nest_start(msg, NL80211_ATTR_TESTDATA); + if (!key) { + fprintf(stderr, "fail to nla_nest_start()\n"); + return 1; + } + + NLA_PUT_U32(msg, WL1271_TM_ATTR_CMD_ID, WL1271_TM_CMD_TEST); + NLA_PUT(msg, WL1271_TM_ATTR_DATA, sizeof(prms), &prms); + + nla_nest_end(msg, key); + + return 0; + +nla_put_failure: + fprintf(stderr, "%s> building message failed\n", __func__); + return 2; +} + +COMMAND(wl18xx_plt, stop_tx, "", + NL80211_CMD_TESTMODE, 0, CIB_NETDEV, plt_wl18xx_stop_tx, + "Stop TX transmissions for PLT.\n"); + diff --git a/wl18xx_plt.h b/wl18xx_plt.h new file mode 100644 index 0000000..48108cd --- /dev/null +++ b/wl18xx_plt.h @@ -0,0 +1,252 @@ +/* + * This file is part of calibrator + * + * Copyright (C) 2011 Texas Instruments + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __WL18XX_PLT_H__ +#define __WL18XX_PLT_H__ + +enum wl18xx_test_cmds { + /* 0x1 */ WL18XX_TEST_CMD_PD_BUFFER_CAL = 0x1, + /* 0x2 */ WL18XX_TEST_CMD_P2G_CAL, + /* 0x3 */ WL18XX_TEST_CMD_RX_PLT_ENTER, + /* 0x4 */ WL18XX_TEST_CMD_RX_PLT_CAL, + /* 0x5 */ WL18XX_TEST_CMD_RX_PLT_EXIT, + /* 0x6 */ WL18XX_TEST_CMD_RX_PLT_GET, + /* 0x7 */ WL18XX_TEST_CMD_FCC, + /* 0x8 */ WL18XX_TEST_CMD_TELEC, + /* 0x9 */ WL18XX_TEST_CMD_STOP_TX, + /* 0xa */ WL18XX_TEST_CMD_PLT_TEMPLATE, + /* 0xb */ WL18XX_TEST_CMD_PLT_GAIN_ADJUST, + /* 0xc */ WL18XX_TEST_CMD_PLT_GAIN_GET, + /* 0xd */ WL18XX_TEST_CMD_CHANNEL_TUNE_OLD, + /* 0xe */ WL18XX_TEST_CMD_FREE_RUN_RSSI, + /* 0xf */ WL18XX_TEST_CMD_DEBUG, + /* 0x10 */ WL18XX_TEST_CMD_CLPC_COMMANDS, + /* 0x11 */ WL18XX_TEST_CMD_RESERVED, + /* 0x12 */ WL18XX_TEST_CMD_RX_STAT_STOP, + /* 0x13 */ WL18XX_TEST_CMD_RX_STAT_START, + /* 0x14 */ WL18XX_TEST_CMD_RX_STAT_RESET, + /* 0x15 */ WL18XX_TEST_CMD_RX_STAT_GET, + /* 0x16 */ WL18XX_TEST_CMD_LOOPBACK_START, + /* 0x17 */ WL18XX_TEST_CMD_LOOPBACK_STOP, + /* 0x18 */ WL18XX_TEST_CMD_GET_FW_VERSIONS, + /* 0x19 */ WL18XX_TEST_CMD_INI_FILE_RADIO_PARAM, + /* 0x1a */ WL18XX_TEST_CMD_RUN_CALIBRATION_TYPE, + /* 0x1b */ WL18XX_TEST_CMD_TX_GAIN_ADJUST, + /* 0x1c */ WL18XX_TEST_CMD_UPDATE_PD_BUFFER_ERRORS, + /* 0x1d */ WL18XX_TEST_CMD_UPDATE_PD_REFERENCE_POINT, + /* 0x1e */ WL18XX_TEST_CMD_INI_FILE_GENERAL_PARAM, + /* 0x1f */ WL18XX_TEST_CMD_SET_EFUSE, + /* 0x20 */ WL18XX_TEST_CMD_GET_EFUSE, + /* 0x21 */ WL18XX_TEST_CMD_TEST_TONE, + /* 0x22 */ WL18XX_TEST_CMD_POWER_MODE, + /* 0x23 */ WL18XX_TEST_CMD_SMART_REFLEX, + /* 0x24 */ WL18XX_TEST_CMD_CHANNEL_RESPONSE, + /* 0x25 */ WL18XX_TEST_CMD_DCO_ITRIM_FEATURE, + /* 0x26 */ WL18XX_TEST_CMD_START_TX_SIMULATION, + /* 0x27 */ WL18XX_TEST_CMD_STOP_TX_SIMULATION, + /* 0x28 */ WL18XX_TEST_CMD_START_RX_SIMULATION, + /* 0x29 */ WL18XX_TEST_CMD_STOP_RX_SIMULATION, + /* 0x2a */ WL18XX_TEST_CMD_GET_RX_STATISTICS, + /* 0x2b */ /*WL18XX_TEST_CMD_INI_FILE_RF_EXTENDED_PARAM*/ + /* 0x2c */ WL18XX_TEST_CMD_SET_NVS_VERSION, + /* 0x2d */ WL18XX_TEST_CMD_CHANNEL_TUNE, + /* 0x2e */ WL18XX_TEST_CMD_TX_POWER, + /* 0x2f */ WL18XX_TEST_CMD_SET_ANTENNA_MODE_24G, + /* 0x30 */ WL18XX_TEST_CMD_GET_CALIB_RESULT, + /* 0x31 */ WL18XX_TEST_CMD_SET_ANTENNA_MODE_5G, + /* 0x32 */ WL18XX_TEST_CMD_PHY_ADDR_READ, + /* 0x33 */ WL18XX_TEST_CMD_PHY_ADDR_WRITE, + /* 0x34 */ WL18XX_TEST_CMD_START_TX_TONE, + /* 0x35 */ WL18XX_TEST_CMD_STOP_TX_TONE, + /* 0x36 */ WL18XX_TEST_CMD_START_RF_PARAMS_SET, + /* 0x37 */ WL18XX_TEST_CMD_STOP_RF_PARAMS_SET, + /* 0x38 */ WL18XX_TEST_CMD_REG_DOMAIN_UPDATE, + /* 0x39 */ WL18XX_TEST_CMD_5GHZ_ANTENNA_DIVERSITY, +}; + +struct wl18xx_cmd_channel_tune { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le16 radio_status; + __u8 channel; + __u8 band; + __u8 bandwidth; + __u8 rfSetIdx; + __u8 padding[2]; +} __attribute__((packed)); + +struct wl18xx_cmd_start_rx { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __le32 magic_num; + __u8 src_addr[MAC_ADDR_LEN]; + __u8 dst_addr[MAC_ADDR_LEN]; +} __attribute__((packed)); + +struct wl18xx_cmd_stop_rx { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; +} __attribute__((packed)); + +struct wl18xx_cmd_rx_stats { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __le32 total; + __le32 errors; + __le32 addr_mm; + __le32 good; + __le32 rssi_soc; + __le32 rssi_ant; +} __attribute__((packed)); + +struct wl18xx_cmd_start_tx { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __le32 delay; + __le32 rate; + __le32 size; + __le32 mode; + __le32 data_type; + __le32 gi; + __le32 options1; + __le32 options2; + __u8 src_addr[MAC_ADDR_LEN]; + __u8 dst_addr[MAC_ADDR_LEN]; + __le32 bandwidth; + __le32 padding; +} __attribute__((packed)); + +struct wl18xx_cmd_stop_tx { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; +} __attribute__((packed)); + +struct wl18xx_cmd_set_tx_power { + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __s32 mac_des_pwr; + __le32 mac_lvl_idx; + __le32 freq_band; + __le32 freq_prim_chan_num; + __le32 freq_prim_chan_loc; + __le32 mac_ant_select; + __le32 mac_non_srv; + __le32 mac_chan_lim_dis; + __le32 mac_fem_lim_dis; + __le32 mac_gain_calc_mode; + __le32 mac_analog_gain_control_idx; + __le32 mac_post_dpd_gain; +} __attribute__((packed)); + +struct wl18xx_cmd_set_antenna_mode_24G { /* TEST_CMD_SET_ANTENNA_MODE24G */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __u8 mac_prim_rx_chain; + __u8 mac_prim_tx_chain; + __u8 mac_rx_chain1_en; + __u8 mac_rx_chain2_en; + __u8 mac_tx_chain1_en; + __u8 mac_tx_chain2_en; + __u8 res1; + __u8 res2; +} __attribute__((packed)); + +struct wl18xx_cmd_set_antenna_mode_5G { /* TEST_CMD_SET_ANTENNA_MODE5G */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __u8 mac_prim_rx_chain; + __u8 mac_rx_chain1_en; + __u8 mac_rx_chain2_en; + __u8 mac_tx_chain1_en; +} __attribute__((packed)); + +struct wl18xx_cmd_phy_reg_read { /* TEST_CMD_PHY_REG_READ */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __le32 data; + __le32 addr; +} __attribute__((packed)); + +struct wl18xx_cmd_phy_reg_write { /* TEST_CMD_PHY_REG_WRITE */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __le32 data; + __le32 addr; +} __attribute__((packed)); + +struct wl18xx_cmd_phy_tx_tone_start { /* TEST_CMD_START_TX_TONE */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __u8 mode; + __s8 bin_index; + __u8 trigger_iqram_recording; + __u8 sig_gen_cw_en; + __u8 sig_gen_mod_en; + __u8 ant_mode; + __u8 set_rx_aux_on; + __u8 gain_index; +} __attribute__((packed)); + +struct wl18xx_cmd_phy_tx_tone_stop { /* TEST_CMD_STOP_TX_TONE */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; +} __attribute__((packed)); + +struct wl18xx_cmd_set_antenna_diversity_5G { /* WL18XX_TEST_CMD_5GHZ_ANTENNA_DIVERSITY */ + struct wl1271_cmd_header header; + struct wl1271_cmd_test_header test; + + __le32 radio_status; + + __u8 mode; + __u8 padding[3]; + +} __attribute__((packed)); + +#endif /* __WL18XX_PLT_H__ */ -- cgit v1.1