From 8fea084fa76f76159bcc110699eb5a91854e6f19 Mon Sep 17 00:00:00 2001 From: Nagarajan Maran Date: Thu, 10 Nov 2022 18:52:12 +0530 Subject: [PATCH] iw: add support for sawf Add configure, disable and view support for SAWF service classes. Below is the command to configure a service class - iw phy service_class create [min_tput ] [max_tput ] [burst_size ] [service_interval ] [delay_bound ] [msdu_ttl ] [priority ] [tid ] [msdu_loss ] Below is the command to disable a service class - iw phy service_class delete Below is the command to view a specific service class - iw phy service_class view Below is the command to view all service classes - iw phy service_class view Signed-off-by: Karthikeyan Kathirvel Co-Developed-by: Nagarajan Maran Signed-off-by: Nagarajan Maran --- iw.h | 17 +++++ sawf.c | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 sawf.c --- a/iw.h +++ b/iw.h @@ -69,6 +69,12 @@ enum id_input { II_WDEV, }; +enum qca_nl80211_vendor_subcmds { + QCA_NL80211_VENDOR_SUBCMD_ROAM_EVENTS = 203, + QCA_NL80211_VENDOR_SUBCMD_SVC_CREATE = 204, + QCA_NL80211_VENDOR_SUBCMD_SVC_DISABLE = 205, + QCA_NL80211_VENDOR_SUBCMD_SVC_VIEW = 206, +}; /* Attributes for data used by * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION and * QCA_NL80211_VENDOR_SUBCMD_GET_WIFI_CONFIGURATION subcommands. @@ -82,6 +88,26 @@ enum qca_wlan_vendor_attr_config { QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1, }; +enum qca_wlan_vendor_sawf_attr_config { + QCA_WLAN_VENDOR_ATTR_SAWF_SERVICE_CLASSES = 1, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_APP_NAME, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID, + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS, + + /* keep last */ + QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_MAX = + QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_AFTER_LAST - 1, +}; + #define HANDLER_RET_USAGE 1 #define HANDLER_RET_DONE 3 --- /dev/null +++ b/sawf.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved. + + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "nl80211.h" +#include "iw.h" + +#define OUI_QCA 0x001374 +#define MAX_OPTIONAL_STRINGS 9 +#define MAX_STRING_SIZE 60 +#define MAX_RANGE(type) pow(2, 8 * sizeof(type)) - 1 +#define SVC_CREATE_MIN_ARGUMENTS 2 +#define SVC_CREATE_MAX_ARGUMENTS 20 +#define SVC_DISABLE_MAX_ARGUMENTS 1 +#define SVC_VIEW_MAX_ARGUMENTS 1 +#define APP_NAME_MAX_BYTES 64 + +struct sawf_disable_param { + uint8_t svc_id; +}; + +SECTION(service_class); + +static int handle_service_class_create(struct nl80211_state *state, + struct nl_msg *msg, int argc, + char **argv, enum id_input id) +{ + struct nlattr *service_class; + char app_name[APP_NAME_MAX_BYTES]; + int parsed = 0, param_count; + char *end; + unsigned long value; + char service_check[MAX_OPTIONAL_STRINGS][MAX_STRING_SIZE] = {"min_tput", + "max_tput", + "burst_size", + "service_interval", + "delay_bound", + "msdu_ttl", + "priority", + "tid", + "msdu_loss"}; + + if (argc < SVC_CREATE_MIN_ARGUMENTS || argc > SVC_CREATE_MAX_ARGUMENTS || + (argc % SVC_CREATE_MIN_ARGUMENTS) != 0) + goto err; + + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA); + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SVC_CREATE); + + service_class = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!service_class) + return -ENOBUFS; + + errno = 0; + value = strtoul(argv[0], &end, 10); + if (*end != '\0' || value > MAX_RANGE(uint8_t) || errno == ERANGE) + goto err; + + strlcpy(app_name, argv[1], APP_NAME_MAX_BYTES); + + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID, value); + nla_put(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_APP_NAME, sizeof(app_name), app_name); + + parsed += 2; + + for (errno = 0; parsed < argc; parsed += 2, errno = 0) { + value = strtoul(argv[parsed + 1], &end, 10); + if (*end != '\0' || value > MAX_RANGE(uint32_t) || errno == ERANGE) + goto err; + + for (param_count = 0; param_count < MAX_OPTIONAL_STRINGS; + param_count++) { + if (!strcmp(argv[parsed], service_check[param_count])) + break; + } + + if (param_count == MAX_OPTIONAL_STRINGS) + goto err; + + switch (param_count + QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP) { + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND, + value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID, value); + break; + case QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS: + nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS, + value); + break; + } + } + + nla_nest_end(msg, service_class); + return 0; +err: + printf("Invalid SAWF service class command format: Usage\n"); + printf("\t iw phy service_class create "); + printf("[min_tput ] [max_tput ] "); + printf("[burst_size ] [service_interval ] "); + printf("[delay_bound ] [msdu_ttl ] "); + printf("[priority ] [tid ] [msdu_loss ]\n"); + + return -EINVAL; +} + +COMMAND(service_class, create, " [min_tput ]" + " [max_tput ] [burst_size ]" + " [service_interval ] [delay_bound ]" + " [msdu_ttl ] [priority ] [tid ]" + " [msdu_loss ]", NL80211_CMD_VENDOR, 0, CIB_PHY, + handle_service_class_create, "."); + +static int handle_service_class_disable(struct nl80211_state *state, + struct nl_msg *msg, int argc, + char **argv, enum id_input id) +{ + struct nlattr *service_class; + unsigned long value; + char *end; + + if (argc != SVC_DISABLE_MAX_ARGUMENTS) + goto err; + + errno = 0; + value = strtoul(argv[0], &end, 10); + if (*end != '\0' || value > MAX_RANGE(uint8_t) || errno == ERANGE) + goto err; + + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA); + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SVC_DISABLE); + + service_class = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!service_class) + return -ENOBUFS; + + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID, value); + + nla_nest_end(msg, service_class); + return 0; + +err: + printf("Invalid SAWF service class command format: Usage\n"); + printf("\t iw phy service_class disable \n"); + return -EINVAL; +} + +COMMAND(service_class, disable, "", NL80211_CMD_VENDOR, 0, CIB_PHY, + handle_service_class_disable, "."); + +static int print_sawf_service_classes(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; + struct nlattr *sawf_service_classes; + struct nlattr *sawf_service_class; + struct nlattr *attrs[QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + int sawf_service_classes_len = 0; + char *app_name = NULL; + static struct nla_policy sawf_policy[QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID] = {.type = NLA_U8}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_APP_NAME] = {.type = NLA_STRING}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID] = {.type = NLA_U32}, + [QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS] = {.type = NLA_U32}, + }; + + nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb_msg[NL80211_ATTR_VENDOR_DATA]) { + sawf_service_classes = nla_data(tb_msg[NL80211_ATTR_VENDOR_DATA]); + sawf_service_classes_len = nla_len(tb_msg[NL80211_ATTR_VENDOR_DATA]); + } + else + return NL_SKIP; + + if (nla_type(sawf_service_classes) != QCA_WLAN_VENDOR_ATTR_SAWF_SERVICE_CLASSES ) + return NL_SKIP; + + nla_for_each_nested(sawf_service_class, sawf_service_classes, + sawf_service_classes_len) { + nla_parse_nested(attrs, + QCA_WLAN_VENDOR_SAWF_ATTR_CONFIG_MAX, + sawf_service_class, sawf_policy); + + printf("***********************************\n"); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID]) + printf("Service ID\t: %u\n", + nla_get_u8(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_APP_NAME]) { + app_name = nla_data(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_APP_NAME]); + printf("App Name\t: %s\n", app_name); + } + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP]) + printf("Min throughput\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MIN_TP])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP]) + printf("Max throughput\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MAX_TP])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE]) + printf("Burst Size\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_BURST_SIZE])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL]) + printf("Service Interval: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_INTERVAL])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND]) + printf("Delay Bound\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_DELAY_BOUND])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL]) + printf("MSDU TTL\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_TTL])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO]) + printf("Priority\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_PRIO])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID]) + printf("TID\t\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_TID])); + if (attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS]) + printf("MSDU Loss Rate\t: %u\n", + nla_get_u32(attrs[QCA_WLAN_VENDOR_ATTR_SAWF_SVC_MSDU_RATE_LOSS])); + } + return NL_SKIP; +} + +static int handle_service_class_dump(struct nl80211_state *state, + struct nl_msg *msg, int argc, + char **argv, enum id_input id) +{ + struct nlattr *service_class; + unsigned long value = 0; + char *end; + + if (argc > SVC_VIEW_MAX_ARGUMENTS) { + goto err; + } + else if (argc == SVC_VIEW_MAX_ARGUMENTS) { + errno = 0; + value = strtoul(argv[0], &end, 10); + if (*end != '\0' || value > MAX_RANGE(uint8_t) || errno == ERANGE) + goto err; + } + + nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA); + nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, + QCA_NL80211_VENDOR_SUBCMD_SVC_VIEW); + + service_class = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); + if (!service_class) + return -ENOBUFS; + + if (argc) + nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_SAWF_SVC_ID, value); + + nla_nest_end(msg, service_class); + + register_handler(print_sawf_service_classes, NULL); + return 0; +err: + printf("Invalid SAWF service class command format: Usage\n"); + printf("\t iw phy service_class view []\n"); + return -EINVAL; +} + +COMMAND(service_class, view, "[]", NL80211_CMD_VENDOR, NLM_F_DUMP, CIB_PHY, + handle_service_class_dump, ".");