[][Add mwctl support for mtk opensource/proprietary driver]

[Description]
Add mwctl support for mtk opensource/proprietary driver, mwctl is used
to replace iwpriv and mt76-vendor tools, mwctl uses nl80211 to
communicate with driver.

[Release-log]

Change-Id: If6deb6bfb9582212fa6ea8e5e589b478d206a383
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/5648065
diff --git a/feed/mwctl/src/iwpriv_compat.c b/feed/mwctl/src/iwpriv_compat.c
new file mode 100755
index 0000000..22a8dcc
--- /dev/null
+++ b/feed/mwctl/src/iwpriv_compat.c
@@ -0,0 +1,270 @@
+/* Copyright (C) 2021 Mediatek Inc. */

+#define _GNU_SOURCE

+

+#include "mtk_vendor_nl80211.h"

+#include "mt76-vendor.h"

+#include "mwctl.h"

+

+static unsigned char ascii2hex(char *in, unsigned int *out)

+{

+	unsigned int hex_val, val;

+	char *p, asc_val;

+

+	hex_val = 0;

+	p = (char *)in;

+

+	while ((*p) != 0) {

+		val = 0;

+		asc_val = *p;

+

+		if ((asc_val >= 'a') && (asc_val <= 'f'))

+			val = asc_val - 87;

+		else if ((*p >= 'A') && (asc_val <= 'F'))

+			val = asc_val - 55;

+		else if ((asc_val >= '0') && (asc_val <= '9'))

+			val = asc_val - 48;

+		else

+			return 0;

+

+		hex_val = (hex_val << 4) + val;

+		p++;

+	}

+

+	*out = hex_val;

+	return 1;

+}

+

+static int handle_common_command(struct nl_msg *msg,

+			   int argc, char **argv, int attr)

+{

+	void *data;

+	size_t len = 0;

+	char *cmd_str;

+	

+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);

+	if (!data)

+		return -ENOMEM;

+

+	if (argc > 0) {

+		cmd_str = argv[0];

+

+		len = strlen(cmd_str);

+		if (len) {

+			if (nla_put(msg, attr, len, (unsigned char*)cmd_str))

+				return -EMSGSIZE;

+		}

+	}

+	nla_nest_end(msg, data);

+

+	return 0;

+}

+

+int handle_set_command(struct nl_msg *msg, int argc,

+	char **argv, void *ctx)

+{	

+	return handle_common_command(msg, argc, argv, MTK_NL80211_VENDOR_ATTR_VENDOR_SET_CMD_STR);

+}

+

+TOPLEVEL(set, "[param]=[value]", MTK_NL80211_VENDOR_SUBCMD_VENDOR_SET, 0, CIB_NETDEV, handle_set_command,

+	 "this command is used to be compatible with old iwpriv set command, e.g iwpriv ra0 set channel=36");

+

+int show_callback(struct nl_msg *msg, void *cb)

+{

+	struct nlattr *tb[NL80211_ATTR_MAX + 1];

+	struct nlattr *vndr_tb[MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_ATTR_MAX + 1];

+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));

+	char *show_str = NULL;

+	int err = 0;

+

+	err = nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),

+			  genlmsg_attrlen(gnlh, 0), NULL);

+	if (err < 0)

+		return err;

+

+	if (tb[NL80211_ATTR_VENDOR_DATA]) {

+		err = nla_parse_nested(vndr_tb, MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_ATTR_MAX,

+			tb[NL80211_ATTR_VENDOR_DATA], NULL);

+		if (err < 0)

+			return err;

+

+		if (vndr_tb[MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_RSP_STR]) {

+			show_str = nla_data(vndr_tb[MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_RSP_STR]);

+			printf("%s\n", show_str);

+		}		

+	} else

+		printf("no any show rsp string from driver\n");

+

+	return 0;

+}

+

+static int handle_show_command(struct nl_msg *msg, int argc,

+	char **argv, void *ctx)

+{	

+	register_handler(show_callback, NULL);

+	return handle_common_command(msg, argc, argv, MTK_NL80211_VENDOR_ATTR_VENDOR_SHOW_CMD_STR);

+}

+

+TOPLEVEL(show, "param", MTK_NL80211_VENDOR_SUBCMD_VENDOR_SHOW, 0, CIB_NETDEV, handle_show_command,

+	 "this command is used to be compatible with old iwpriv show command, e.g iwpriv ra0 show stainfo");

+

+int stat_callback(struct nl_msg *msg, void *cb)

+{

+	struct nlattr *tb[NL80211_ATTR_MAX + 1];

+	struct nlattr *vndr_tb[MTK_NL80211_VENDOR_ATTR_STATISTICS_ATTR_MAX + 1];

+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));

+	char *show_str = NULL;

+	int err = 0;

+

+	err = nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),

+			  genlmsg_attrlen(gnlh, 0), NULL);

+	if (err < 0)

+		return err;

+

+	if (tb[NL80211_ATTR_VENDOR_DATA]) {

+		err = nla_parse_nested(vndr_tb, MTK_NL80211_VENDOR_ATTR_STATISTICS_ATTR_MAX,

+			tb[NL80211_ATTR_VENDOR_DATA], NULL);

+		if (err < 0)

+			return err;

+

+		if (vndr_tb[MTK_NL80211_VENDOR_ATTR_STATISTICS_STR]) {

+			show_str = nla_data(vndr_tb[MTK_NL80211_VENDOR_ATTR_STATISTICS_STR]);

+			printf("%s\n", show_str);

+		}		

+	} else

+		printf("no any statistic string from driver\n");

+

+	return 0;

+}

+

+static int handle_stat_command(struct nl_msg *msg, int argc,

+	char **argv, void *ctx)

+{	

+	register_handler(stat_callback, NULL);

+	return handle_common_command(msg, 0, NULL, 0);

+}

+

+TOPLEVEL(stat, NULL, MTK_NL80211_VENDOR_SUBCMD_STATISTICS, 0, CIB_NETDEV, handle_stat_command,

+	 "this command is used to be compatible with old iwpriv stat command, e.g iwpriv ra0 stat");

+

+

+int mac_callback(struct nl_msg *msg, void *cb)

+{

+	struct nlattr *tb[NL80211_ATTR_MAX + 1];

+	struct nlattr *vndr_tb[MTK_NL80211_VENDOR_ATTR_MAC_ATTR_MAX + 1];

+	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));

+	char *show_str = NULL;

+	int err = 0;

+

+	err = nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),

+			  genlmsg_attrlen(gnlh, 0), NULL);

+	if (err < 0)

+		return err;

+

+	if (tb[NL80211_ATTR_VENDOR_DATA]) {

+		err = nla_parse_nested(vndr_tb, MTK_NL80211_VENDOR_ATTR_MAC_ATTR_MAX,

+			tb[NL80211_ATTR_VENDOR_DATA], NULL);

+		if (err < 0)

+			return err;

+

+		if (vndr_tb[MTK_NL80211_VENDOR_ATTR_MAC_RSP_STR]) {

+			show_str = nla_data(vndr_tb[MTK_NL80211_VENDOR_ATTR_MAC_RSP_STR]);

+			printf("%s\n", show_str);

+		}		

+	} else

+		printf("no any statistic string from driver\n");

+

+	return 0;

+}

+

+

+static int handle_mac_command(struct nl_msg *msg, int argc,

+	char **argv, void *ctx)

+{

+	void *data;

+	char *ptr, *seg_str, *addr_str, *val_str, *range_str;

+	unsigned char is_write, is_range;

+	unsigned int mac_s = 0, mac_e = 0;

+	unsigned int macVal = 0;

+	struct mac_param param;

+

+	if (!argc)

+		return -EINVAL;

+	

+	data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);

+	if (!data)

+		return -ENOMEM;

+

+	ptr = argv[0];

+

+	while ((seg_str = strsep((char **)&ptr, ",")) != NULL) {

+		is_write = 0;

+		addr_str = seg_str;

+		val_str = NULL;

+		val_str = strchr(seg_str, '=');

+

+		if (val_str != NULL) {

+			*val_str++ = 0;

+			is_write = 1;

+		} else

+			is_write = 0;

+

+		if (addr_str) {

+			range_str = strchr(addr_str, '-');

+

+			if (range_str != NULL) {

+				*range_str++ = 0;

+				is_range = 1;

+			} else

+				is_range = 0;

+

+			if ((ascii2hex(addr_str, &mac_s) == 0)) {

+				printf("Invalid MAC CR Addr, str=%s\n", addr_str);

+				break;

+			}

+

+			if (is_range) {

+				if (ascii2hex(range_str, &mac_e) == 0) {

+					printf("Invalid Range End MAC CR Addr[0x%x], str=%s\n",

+							 mac_e, range_str);

+					break;

+				}

+

+				if (mac_e < mac_s) {

+					printf("Invalid Range MAC Addr[%s - %s] => [0x%x - 0x%x]\n",

+							 addr_str, range_str, mac_s, mac_e);

+					break;

+				}

+			} else

+				mac_e = mac_s;

+		}

+

+		if (val_str) {

+			if ((strlen(val_str) == 0) || ascii2hex(val_str, &macVal) == 0) {

+				printf("Invalid MAC value[0x%s]\n", val_str);

+				break;

+			}

+		}

+

+		memset(&param, 0, sizeof(param));

+		param.start = mac_s;

+		param.value = macVal;

+		param.end = mac_e;

+		if (is_write) {

+			if (nla_put(msg, MTK_NL80211_VENDOR_ATTR_MAC_WRITE_PARAM, sizeof(param), &param))

+				return -EMSGSIZE;

+		} else {

+			if (nla_put(msg, MTK_NL80211_VENDOR_ATTR_MAC_SHOW_PARAM, sizeof(param), &param))

+				return -EMSGSIZE;

+		}

+	}

+

+	nla_nest_end(msg, data);

+	register_handler(mac_callback, NULL);

+	return 0;

+}

+

+

+TOPLEVEL(mac, "addr=hex/addr-addr/addr=hex,addr=hex", MTK_NL80211_VENDOR_SUBCMD_MAC, 0,

+	CIB_NETDEV, handle_mac_command,

+	"this command is used to be compatible with old iwpriv mac, e.g iwpriv ra0 mac 2345");

+