From e42b0b2d48637231fdcc51ad584c1bd786c09832 Mon Sep 17 00:00:00 2001 From: Julian Kornberger Date: Mon, 18 Jul 2016 18:06:12 +0200 Subject: [PATCH] Add package gluon-airtime on ath9k: Works fine on ath10k: Does not return any data --- package/gluon-airtime/Makefile | 40 ++++++ package/gluon-airtime/src/Makefile | 22 +++ package/gluon-airtime/src/airtime-test.c | 32 +++++ package/gluon-airtime/src/airtime.c | 174 +++++++++++++++++++++++ package/gluon-airtime/src/airtime.h | 24 ++++ package/gluon-airtime/src/respondd.c | 69 +++++++++ 6 files changed, 361 insertions(+) create mode 100644 package/gluon-airtime/Makefile create mode 100644 package/gluon-airtime/src/Makefile create mode 100644 package/gluon-airtime/src/airtime-test.c create mode 100644 package/gluon-airtime/src/airtime.c create mode 100644 package/gluon-airtime/src/airtime.h create mode 100644 package/gluon-airtime/src/respondd.c diff --git a/package/gluon-airtime/Makefile b/package/gluon-airtime/Makefile new file mode 100644 index 00000000..479a419e --- /dev/null +++ b/package/gluon-airtime/Makefile @@ -0,0 +1,40 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=gluon-airtime +PKG_VERSION:=1 +PKG_RELEASE:=1 + +PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME) +PKG_BUILD_DEPENDS := respondd + +include $(GLUONDIR)/include/package.mk + +define Package/gluon-airtime + SECTION:=gluon + CATEGORY:=Gluon + TITLE:=Add airtime to respondd + DEPENDS:=+respondd +libnl-tiny +endef + +define Build/Prepare + mkdir -p $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Build/Configure +endef + + +TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/libnl-tiny + +define Build/Compile + CFLAGS="$(TARGET_CFLAGS)" CPPFLAGS="$(TARGET_CPPFLAGS)" $(MAKE) -C $(PKG_BUILD_DIR) $(TARGET_CONFIGURE_OPTS) +endef + + +define Package/gluon-airtime/install + $(INSTALL_DIR) $(1)/lib/gluon/respondd + $(CP) $(PKG_BUILD_DIR)/respondd.so $(1)/lib/gluon/respondd/airtime.so +endef + +$(eval $(call BuildPackage,gluon-airtime)) diff --git a/package/gluon-airtime/src/Makefile b/package/gluon-airtime/src/Makefile new file mode 100644 index 00000000..e265638f --- /dev/null +++ b/package/gluon-airtime/src/Makefile @@ -0,0 +1,22 @@ +ifeq ($(origin CC),default) +CC = gcc +endif + +# standard compliance +CFLAGS += -std=c99 + +# warnings +CFLAGS += -Wall -Wextra -Wformat=2 -Wshadow -Wpointer-arith +CFLAGS += -pedantic + +all: respondd.so + +# sudo apt install libnl-3-dev +airtime-test: airtime-test.c airtime.c airtime.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -fPIC -D_GNU_SOURCE -lnl-tiny -o $@ airtime.c airtime-test.c $(LDLIBS) + +respondd.so: respondd.c airtime.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -lnl-tiny -o $@ airtime.c respondd.c $(LDLIBS) + +clean: + rm -rf *.so airtime-test diff --git a/package/gluon-airtime/src/airtime-test.c b/package/gluon-airtime/src/airtime-test.c new file mode 100644 index 00000000..088a9c25 --- /dev/null +++ b/package/gluon-airtime/src/airtime-test.c @@ -0,0 +1,32 @@ +#include +#include /* sleep */ +#include "airtime.h" + +void print_result(struct airtime_result *); + +int main(int argc, char *argv[]) { + struct airtime *a; + + if (argc != 3) { + fprintf(stderr,"Usage: %s \n", argv[0]); + return 1; + } + + while (1) { + a = get_airtime(argv[1], argv[2]); + print_result(&a->radio0); + print_result(&a->radio1); + sleep(1); + } +} + +void print_result(struct airtime_result *result){ + printf("freq=%d\tnoise=%d\tbusy=%lld\tactive=%lld\trx=%lld\ttx=%lld\n", + result->frequency, + result->noise, + result->busy_time.current, + result->active_time.current, + result->rx_time.current, + result->tx_time.current + ); +} diff --git a/package/gluon-airtime/src/airtime.c b/package/gluon-airtime/src/airtime.c new file mode 100644 index 00000000..7d3fbed9 --- /dev/null +++ b/package/gluon-airtime/src/airtime.c @@ -0,0 +1,174 @@ +/* + Copyright (c) 2016, Julian Kornberger + Martin Müller + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include +#include +#include +#include + +#include "airtime.h" + +static struct airtime cur_airtime = { + { .frequency = 0 }, + { .frequency = 0 }, +}; + +/* + * Excerpt from nl80211.h: + * 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) + * @NL80211_SURVEY_INFO_IN_USE: channel is currently being used + * @NL80211_SURVEY_INFO_CHANNEL_TIME: amount of time (in ms) that the radio + * spent on this channel + * @NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY: amount of the time the primary + * channel was sensed busy (either due to activity or energy detect) + * @NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY: amount of time the extension + * channel was sensed busy + * @NL80211_SURVEY_INFO_CHANNEL_TIME_RX: amount of time the radio spent + * receiving data + * @NL80211_SURVEY_INFO_CHANNEL_TIME_TX: amount of time the radio spent + * transmitting data + * @NL80211_SURVEY_INFO_MAX: highest survey info attribute number + * currently defined + * @__NL80211_SURVEY_INFO_AFTER_LAST: internal use + */ + +static int survey_airtime_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1]; + static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = { + [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 }, + }; + + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct airtime_result *result = (struct airtime_result *) arg; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),genlmsg_attrlen(gnlh, 0), NULL); + + if (!tb[NL80211_ATTR_SURVEY_INFO]) { + fprintf(stderr, "survey data missing!\n"); + goto abort; + } + + if(nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX, tb[NL80211_ATTR_SURVEY_INFO], survey_policy)) { + fprintf(stderr, "failed to parse nested attributes!\n"); + goto abort; + } + + // Channel active? + if(!sinfo[NL80211_SURVEY_INFO_IN_USE]){ + goto abort; + } + + uint64_t frequency = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]); + + if (frequency != result->frequency) { + // channel changed, restart at zero + result->frequency = frequency; + result->active_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]); + result->busy_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]); + result->rx_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]); + result->tx_time.offset = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]); + result->active_time.current = 0; + result->busy_time.current = 0; + result->rx_time.current = 0; + result->tx_time.current = 0; + } else { + result->active_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) - result->active_time.offset; + result->busy_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) - result->busy_time.offset; + result->rx_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) - result->rx_time.offset; + result->tx_time.current = nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) - result->tx_time.offset; + } + + result->noise = nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]); + +abort: + return NL_SKIP; +} + +static int get_airtime_for_interface(struct airtime_result *result, const char *interface) { + int error = 0; + int ctrl, ifx, flags; + struct nl_sock *sk = NULL; + struct nl_msg *msg = NULL; + enum nl80211_commands cmd; + +#define CHECK(x) { if (!(x)) { fprintf(stderr, "airtime.c: error on line %d\n", __LINE__); error = 1; goto out; } } + + CHECK(sk = nl_socket_alloc()); + CHECK(genl_connect(sk) >= 0); + + CHECK(ctrl = genl_ctrl_resolve(sk, NL80211_GENL_NAME)); + CHECK(nl_socket_modify_cb(sk, NL_CB_VALID, NL_CB_CUSTOM, survey_airtime_handler, result) == 0); + CHECK(msg = nlmsg_alloc()); + + /* device does not exist */ + if (!(ifx = if_nametoindex(interface))){ + error = -1; + goto out; + } + + cmd = NL80211_CMD_GET_SURVEY; + flags = 0; + flags |= NLM_F_DUMP; + + /* TODO: check return? */ + genlmsg_put(msg, 0, 0, ctrl, 0, flags, cmd, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifx); + + CHECK(nl_send_auto_complete(sk, msg) >= 0); + CHECK(nl_recvmsgs_default(sk) >= 0); + +#undef CHECK + +nla_put_failure: +out: + if (msg) + nlmsg_free(msg); + + if (sk) + nl_socket_free(sk); + + return error; +} + +struct airtime* get_airtime(const char *wifi_0_dev, const char *wifi_1_dev) { + get_airtime_for_interface(&cur_airtime.radio0, wifi_0_dev); + get_airtime_for_interface(&cur_airtime.radio1, wifi_1_dev); + + return &cur_airtime; +} diff --git a/package/gluon-airtime/src/airtime.h b/package/gluon-airtime/src/airtime.h new file mode 100644 index 00000000..4a1347c1 --- /dev/null +++ b/package/gluon-airtime/src/airtime.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +struct airtime_time { + uint64_t current; + uint64_t offset; +}; + +struct airtime_result { + uint32_t frequency; + uint8_t noise; + struct airtime_time active_time; + struct airtime_time busy_time; + struct airtime_time rx_time; + struct airtime_time tx_time; +}; + +struct airtime { + struct airtime_result radio0; + struct airtime_result radio1; +}; + +struct airtime* get_airtime(const char *radio0, const char *radio1); diff --git a/package/gluon-airtime/src/respondd.c b/package/gluon-airtime/src/respondd.c new file mode 100644 index 00000000..0f62afd9 --- /dev/null +++ b/package/gluon-airtime/src/respondd.c @@ -0,0 +1,69 @@ +#include +#include +#include +#include + +#include "airtime.h" + +static const char const *wifi_0_dev = "client0"; +static const char const *wifi_1_dev = "client1"; + +void fill_airtime_json(struct airtime_result *air, struct json_object* wireless){ + struct json_object *result = NULL, *obj = NULL; + + obj = json_object_new_object(); + if(!obj) + goto error; +#define JSON_ADD_INT64(value,key) {result = json_object_new_int64(value); json_object_object_add(obj,key,result);} + result = json_object_new_int(air->frequency); + if(!result) + goto error; + json_object_object_add(obj,"frequency",result); + + JSON_ADD_INT64(air->active_time.current,"active") + JSON_ADD_INT64(air->busy_time.current,"busy") + JSON_ADD_INT64(air->rx_time.current,"rx") + JSON_ADD_INT64(air->tx_time.current,"tx") + + result = json_object_new_int(air->noise); + json_object_object_add(obj,"noise",result); + +error: + if(air->frequency >= 2400 && air->frequency < 2500) + json_object_object_add(wireless, "airtime24", obj); + else if (air->frequency >= 5000 && air->frequency < 6000) + json_object_object_add(wireless, "airtime5", obj); +} + +static struct json_object *respondd_provider_statistics(void) { + struct airtime *airtime = NULL; + struct json_object *result = NULL, *wireless = NULL; + + airtime = get_airtime(wifi_0_dev, wifi_1_dev); + if (!airtime) + return NULL; + + result = json_object_new_object(); + if (!result) + return NULL; + + wireless = json_object_new_object(); + if (!wireless){ + json_object_put(result); + return NULL; + } + + if (airtime->radio0.frequency) + fill_airtime_json(&airtime->radio0, wireless); + + if (airtime->radio1.frequency) + fill_airtime_json(&airtime->radio1, wireless); + + json_object_object_add(result, "wireless", wireless); + return result; +} + +const struct respondd_provider_info respondd_providers[] = { + {"statistics", respondd_provider_statistics}, + {0, 0}, +};