]> git.ipfire.org Git - thirdparty/iw.git/commitdiff
wowlan support
authorJohannes Berg <johannes.berg@intel.com>
Tue, 12 Apr 2011 11:04:50 +0000 (13:04 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 9 May 2011 15:11:26 +0000 (17:11 +0200)
Makefile
info.c
iw.h
util.c
wowlan.c [new file with mode: 0644]

index bd6ca1550c2263140b5c64258318bb5cd1cca5f0..e3ee2a87a44c1737f23cf69b49d814bd48bd0030 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,7 @@ OBJS = iw.o genl.o event.o info.o phy.o \
        interface.o ibss.o station.o survey.o util.o \
        mesh.o mpath.o scan.o reg.o version.o \
        reason.o status.o connect.o link.o offch.o ps.o cqm.o \
-       bitrate.o
+       bitrate.o wowlan.o
 OBJS += sections.o
 ALL = iw
 
diff --git a/info.c b/info.c
index fac62f2b629c0be31daaab2ceab978e9f9f9ff59..b9c08b5a1fb570f80a0e3710a1d09fea6e8d18da 100644 (file)
--- a/info.c
+++ b/info.c
@@ -222,8 +222,42 @@ static int print_phy_handler(struct nl_msg *msg, void *arg)
                }
        }
 
-       if (tb_msg[NL80211_ATTR_SUPPORT_IBSS_RSN]) {
+       if (tb_msg[NL80211_ATTR_SUPPORT_IBSS_RSN])
                printf("\tDevice supports RSN-IBSS.\n");
+
+       if (tb_msg[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]) {
+               struct nlattr *tb_wowlan[NUM_NL80211_WOWLAN_TRIG];
+               static struct nla_policy wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
+                       [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
+                       [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
+                       [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
+                       [NL80211_WOWLAN_TRIG_PKT_PATTERN] = {
+                               .minlen = sizeof(struct nl80211_wowlan_pattern_support),
+                       },
+               };
+               struct nl80211_wowlan_pattern_support *pat;
+               int err;
+
+               err = nla_parse_nested(tb_wowlan, MAX_NL80211_WOWLAN_TRIG,
+                                      tb_msg[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED],
+                                      wowlan_policy);
+               printf("\tWoWLAN support:");
+               if (err) {
+                       printf(" <failed to parse>\n");
+               } else {
+                       printf("\n");
+                       if (tb_wowlan[NL80211_WOWLAN_TRIG_ANY])
+                               printf("\t\t * any (device continues operating)\n");
+                       if (tb_wowlan[NL80211_WOWLAN_TRIG_DISCONNECT])
+                               printf("\t\t * disconnect\n");
+                       if (tb_wowlan[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+                               printf("\t\t * magic packet\n");
+                       if (tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
+                               pat = nla_data(tb_wowlan[NL80211_WOWLAN_TRIG_PKT_PATTERN]);
+                               printf("\t\t * up to %u patterns of %u-%u bytes\n",
+                                       pat->max_patterns, pat->min_pattern_len, pat->max_pattern_len);
+                       }
+               }
        }
 
        return NL_SKIP;
diff --git a/iw.h b/iw.h
index b0bc4898f2432e1d97db71d022bcb38ecc7b8f4a..616fa04c579aee7e00613f2e2b7f088ecec72b22 100644 (file)
--- a/iw.h
+++ b/iw.h
@@ -56,6 +56,7 @@ struct cmd {
 };
 
 #define ARRAY_SIZE(ar) (sizeof(ar)/sizeof(ar[0]))
+#define DIV_ROUND_UP(x, y) (((x) + (y - 1)) / (y))
 
 #define __COMMAND(_section, _symname, _name, _args, _nlcmd, _flags, _hidden, _idby, _handler, _help, _sel)\
        static struct cmd                                               \
@@ -127,6 +128,8 @@ __u32 __do_listen_events(struct nl80211_state *state,
 
 int mac_addr_a2n(unsigned char *mac_addr, char *arg);
 void mac_addr_n2a(char *mac_addr, unsigned char *arg);
+int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len,
+                  unsigned char **mask);
 unsigned char *parse_hex(char *hex, size_t *outlen);
 
 int parse_keys(struct nl_msg *msg, char **argv, int argc);
diff --git a/util.c b/util.c
index 7ff866f9037eb7a44d0f31c86bba913eecad2cc6..c0599c9a7c16c08b20fcd0562c801be1c4b60bf4 100644 (file)
--- a/util.c
+++ b/util.c
@@ -48,41 +48,77 @@ int mac_addr_a2n(unsigned char *mac_addr, char *arg)
        return 0;
 }
 
-unsigned char *parse_hex(char *hex, size_t *outlen)
+int parse_hex_mask(char *hexmask, unsigned char **result, size_t *result_len,
+                  unsigned char **mask)
 {
-       size_t len = strlen(hex);
-       unsigned char *result = calloc(len/2 + 2, 1);
+       size_t len = strlen(hexmask) / 2;
+       unsigned char *result_val;
+       unsigned char *result_mask = NULL;
+
        int pos = 0;
 
-       if (!result)
-               return NULL;
+       *result_len = 0;
 
-       *outlen = 0;
+       result_val = calloc(len + 2, 1);
+       if (!result_val)
+               goto error;
+       *result = result_val;
+       if (mask) {
+               result_mask = calloc(DIV_ROUND_UP(len, 8) + 2, 1);
+               if (!result_mask)
+                       goto error;
+               *mask = result_mask;
+       }
 
        while (1) {
-               int temp;
-               char *cp = strchr(hex, ':');
+               char *cp = strchr(hexmask, ':');
                if (cp) {
                        *cp = 0;
                        cp++;
                }
-               if (sscanf(hex, "%x", &temp) != 1)
-                       goto error;
-               if (temp < 0 || temp > 255)
-                       goto error;
 
-               (*outlen)++;
+               if (result_mask && (strcmp(hexmask, "-") == 0 ||
+                                   strcmp(hexmask, "xx") == 0 ||
+                                   strcmp(hexmask, "--") == 0)) {
+                       /* skip this byte and leave mask bit unset */
+               } else {
+                       int temp, mask_pos;
+                       char *end;
+
+                       temp = strtoul(hexmask, &end, 16);
+                       if (*end)
+                               goto error;
+                       if (temp < 0 || temp > 255)
+                               goto error;
+                       result_val[pos] = temp;
+
+                       mask_pos = pos / 8;
+                       if (result_mask)
+                               result_mask[mask_pos] |= 1 << (pos % 8);
+               }
+
+               (*result_len)++;
+               pos++;
 
-               result[pos++] = temp;
                if (!cp)
                        break;
-               hex = cp;
+               hexmask = cp;
        }
 
-       return result;
+       return 0;
  error:
-       free(result);
-       return NULL;
+       free(result_val);
+       free(result_mask);
+       return -1;
+}
+
+unsigned char *parse_hex(char *hex, size_t *outlen)
+{
+       unsigned char *result;
+
+       if (parse_hex_mask(hex, &result, outlen, NULL))
+               return NULL;
+       return result;
 }
 
 static const char *ifmodes[NL80211_IFTYPE_MAX + 1] = {
diff --git a/wowlan.c b/wowlan.c
new file mode 100644 (file)
index 0000000..bdfa6a9
--- /dev/null
+++ b/wowlan.c
@@ -0,0 +1,179 @@
+#include <net/if.h>
+#include <errno.h>
+#include <string.h>
+
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netlink/msg.h>
+#include <netlink/attr.h>
+
+#include "nl80211.h"
+#include "iw.h"
+
+SECTION(wowlan);
+
+static int handle_wowlan_enable(struct nl80211_state *state, struct nl_cb *cb,
+                               struct nl_msg *msg, int argc, char **argv)
+{
+       struct nlattr *wowlan, *pattern;
+       struct nl_msg *patterns = NULL;
+       enum {
+               PS_REG,
+               PS_PAT,
+       } parse_state = PS_REG;
+       int err = -ENOBUFS;
+       unsigned char *pat, *mask;
+       size_t patlen;
+       int patnum = 0;
+
+       wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
+       if (!wowlan)
+               return -ENOBUFS;
+
+       while (argc) {
+               switch (parse_state) {
+               case PS_REG:
+                       if (strcmp(argv[0], "any") == 0)
+                               NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
+                       else if (strcmp(argv[0], "disconnect") == 0)
+                               NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
+                       else if (strcmp(argv[0], "magic-packet") == 0)
+                               NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
+                       else if (strcmp(argv[0], "patterns") == 0) {
+                               parse_state = PS_PAT;
+                               patterns = nlmsg_alloc();
+                               if (!patterns) {
+                                       err = -ENOMEM;
+                                       goto nla_put_failure;
+                               }
+                       } else {
+                               err = 1;
+                               goto nla_put_failure;
+                       }
+                       break;
+               case PS_PAT:
+                       if (parse_hex_mask(argv[0], &pat, &patlen, &mask)) {
+                               err = 1;
+                               goto nla_put_failure;
+                       }
+                       pattern = nla_nest_start(patterns, ++patnum);
+                       NLA_PUT(patterns, NL80211_WOWLAN_PKTPAT_MASK,
+                               DIV_ROUND_UP(patlen, 8), mask);
+                       NLA_PUT(patterns, NL80211_WOWLAN_PKTPAT_PATTERN,
+                               patlen, pat);
+                       nla_nest_end(patterns, pattern);
+                       free(mask);
+                       free(pat);
+                       break;
+               }
+               argv++;
+               argc--;
+       }
+
+       if (patterns)
+               nla_put_nested(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
+                               patterns);
+
+       nla_nest_end(msg, wowlan);
+       err = 0;
+ nla_put_failure:
+       nlmsg_free(patterns);
+       return err;
+}
+COMMAND(wowlan, enable, "[any] [disconnect] [magic-packet] [patterns <pattern>*]",
+       NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_enable,
+       "Enable WoWLAN with the given triggers.\n"
+       "Each pattern is given as a bytestring with '-' in places where any byte\n"
+       "may be present, e.g. 00:11:22:-:44 will match 00:11:22:33:44 and\n"
+       "00:11:22:33:ff:44 etc.");
+
+
+static int handle_wowlan_disable(struct nl80211_state *state, struct nl_cb *cb,
+                                struct nl_msg *msg, int argc, char **argv)
+{
+       /* just a set w/o wowlan attribute */
+       return 0;
+}
+COMMAND(wowlan, disable, "", NL80211_CMD_SET_WOWLAN, 0, CIB_PHY, handle_wowlan_disable,
+       "Disable WoWLAN.");
+
+
+static int print_wowlan_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *attrs[NL80211_ATTR_MAX + 1];
+       struct nlattr *trig[NUM_NL80211_WOWLAN_TRIG];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *pattern;
+       int rem_pattern;
+
+       nla_parse(attrs, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!attrs[NL80211_ATTR_WOWLAN_TRIGGERS]) {
+               printf("WoWLAN is disabled.\n");
+               return NL_SKIP;
+       }
+
+       /* XXX: use policy */
+       nla_parse(trig, MAX_NL80211_WOWLAN_TRIG,
+                 nla_data(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+                 nla_len(attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
+                 NULL);
+
+       printf("WoWLAN is enabled:\n");
+       if (trig[NL80211_WOWLAN_TRIG_ANY])
+               printf(" * wake up on special any trigger\n");
+       if (trig[NL80211_WOWLAN_TRIG_DISCONNECT])
+               printf(" * wake up on disconnect\n");
+       if (trig[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+               printf(" * wake up on magic packet\n");
+       if (trig[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
+               nla_for_each_nested(pattern,
+                                   trig[NL80211_WOWLAN_TRIG_PKT_PATTERN],
+                                   rem_pattern) {
+                       struct nlattr *patattr[NUM_NL80211_WOWLAN_PKTPAT];
+                       int i, patlen, masklen;
+                       uint8_t *mask, *pat;
+                       nla_parse(patattr, MAX_NL80211_WOWLAN_PKTPAT,
+                                 nla_data(pattern), nla_len(pattern),
+                                 NULL);
+                       if (!patattr[NL80211_WOWLAN_PKTPAT_MASK] ||
+                           !patattr[NL80211_WOWLAN_PKTPAT_PATTERN]) {
+                               printf(" * (invalid pattern specification)\n");
+                               continue;
+                       }
+                       masklen = nla_len(patattr[NL80211_WOWLAN_PKTPAT_MASK]);
+                       patlen = nla_len(patattr[NL80211_WOWLAN_PKTPAT_PATTERN]);
+                       if (DIV_ROUND_UP(patlen, 8) != masklen) {
+                               printf(" * (invalid pattern specification)\n");
+                               continue;
+                       }
+                       printf(" * wake up on pattern: ");
+                       pat = nla_data(patattr[NL80211_WOWLAN_PKTPAT_PATTERN]);
+                       mask = nla_data(patattr[NL80211_WOWLAN_PKTPAT_MASK]);
+                       for (i = 0; i < patlen; i++) {
+                               if (mask[i / 8] & (1 << (i % 8)))
+                                       printf("%.2x", pat[i]);
+                               else
+                                       printf("--");
+                               if (i != patlen - 1)
+                                       printf(":");
+                       }
+                       printf("\n");
+               }
+       }
+
+       return NL_SKIP;
+}
+
+static int handle_wowlan_show(struct nl80211_state *state, struct nl_cb *cb,
+                             struct nl_msg *msg, int argc, char **argv)
+{
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+                 print_wowlan_handler, NULL);
+
+       return 0;
+}
+COMMAND(wowlan, show, "", NL80211_CMD_GET_WOWLAN, 0, CIB_PHY, handle_wowlan_show,
+       "Show WoWLAN status.");