mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-10-29 17:42:41 +00:00
1. porting MorseMicro HaLow driver to support HaLow on EAP112 2. Only support FCC regulation because of hardware limitation 3. Add /etc/init.d/halow-gpio-reset to initialize HaLow chip in early stage 4. Add /etc/uci-defaults/aaa-fix-phy0-to-morse to correct the default uci for HaLow radio. Signed-off-by: Ian Chen <ian77_chen@accton.com>
1996 lines
55 KiB
Diff
1996 lines
55 KiB
Diff
From 67adfbc5b4831ecb62a169370c102e0939ae884e Mon Sep 17 00:00:00 2001
|
|
From: ian77_chen <ian77_chen@accton.com>
|
|
Date: Mon, 10 Mar 2025 15:21:57 +0800
|
|
Subject: [PATCH] patch for Morse Micro iwinfo
|
|
|
|
---
|
|
.../utils/iwinfo/patches/MMiwinfo.patch | 1976 +++++++++++++++++
|
|
1 file changed, 1976 insertions(+)
|
|
create mode 100644 package/network/utils/iwinfo/patches/MMiwinfo.patch
|
|
|
|
diff --git a/package/network/utils/iwinfo/patches/MMiwinfo.patch b/package/network/utils/iwinfo/patches/MMiwinfo.patch
|
|
new file mode 100644
|
|
index 0000000000..c10d4bf881
|
|
--- /dev/null
|
|
+++ b/package/network/utils/iwinfo/patches/MMiwinfo.patch
|
|
@@ -0,0 +1,1976 @@
|
|
+diff --git a/.gitignore b/.gitignore
|
|
+new file mode 100644
|
|
+index 0000000..7f4922f
|
|
+--- /dev/null
|
|
++++ b/.gitignore
|
|
+@@ -0,0 +1,17 @@
|
|
++iwinfo_cli.o
|
|
++iwinfo_lib.o
|
|
++iwinfo_lua.o
|
|
++iwinfo_nl80211.o
|
|
++iwinfo_utils.o
|
|
++iwinfo_wext_scan.o
|
|
++iwinfo_wext.o
|
|
++iwinfo.so
|
|
++libiwinfo.so
|
|
++libiwinfo.so.0
|
|
++iwinfo
|
|
++
|
|
++*.so
|
|
++*.so.*
|
|
++*.o
|
|
++
|
|
++.vscode/
|
|
+diff --git a/Makefile b/Makefile
|
|
+index adb9e73..fac0abf 100644
|
|
+--- a/Makefile
|
|
++++ b/Makefile
|
|
+@@ -37,6 +37,7 @@ ifneq ($(filter nl80211,$(IWINFO_BACKENDS)),)
|
|
+ IWINFO_CLI_LDFLAGS += -lnl-tiny
|
|
+ IWINFO_LIB_LDFLAGS += -lnl-tiny
|
|
+ IWINFO_LIB_OBJ += iwinfo_nl80211.o
|
|
++ IWINFO_LIB_OBJ += iwinfo_morsecli.o dot11ah_channel.o
|
|
+ endif
|
|
+
|
|
+
|
|
+diff --git a/devices.txt b/devices.txt
|
|
+index eded184..397a1cf 100644
|
|
+--- a/devices.txt
|
|
++++ b/devices.txt
|
|
+@@ -192,6 +192,9 @@
|
|
+ 0x02d0 0xa9a6 0x0000 0x0000 0 0 "Cypress" "CYW43455"
|
|
+ 0x02d0 0x4345 0x0000 0x0000 0 0 "Cypress" "CYW43455"
|
|
+ 0x1ae9 0x0310 0x1ae9 0x0000 0 0 "Wilocity" "Wil6210"
|
|
++0x325B 0x0206 0x0000 0x0000 0 0 "Morse Micro" "HaLow WiFi"
|
|
++0x325B 0x0306 0x0000 0x0000 0 0 "Morse Micro" "HaLow WiFi"
|
|
++
|
|
+
|
|
+ # USB devices
|
|
+ # 0x0000 | 0x0000 | vendor id | product id | ...
|
|
+@@ -263,3 +266,4 @@
|
|
+ "ralink,rt3883-wmac" 0 0 "Ralink" "Rt3883"
|
|
+ "ralink,rt5350-wmac" 0 0 "Ralink" "Rt5350"
|
|
+ "ralink,rt7620-wmac" 0 0 "MediaTek" "MT7620"
|
|
++"morse,mm610x-spi" 0 0 "Morse Micro" "HaLow WiFi"
|
|
+diff --git a/dot11ah_channel.c b/dot11ah_channel.c
|
|
+new file mode 100644
|
|
+index 0000000..24dfb0e
|
|
+--- /dev/null
|
|
++++ b/dot11ah_channel.c
|
|
+@@ -0,0 +1,444 @@
|
|
++/*
|
|
++ * Copyright 2022 Morse Micro
|
|
++ *
|
|
++ * The iwinfo library 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.
|
|
++ *
|
|
++ * The iwinfo library 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 the iwinfo library. If not, see http://www.gnu.org/licenses/.
|
|
++*/
|
|
++
|
|
++#include <stdio.h>
|
|
++#include <string.h>
|
|
++#include <stdlib.h>
|
|
++
|
|
++#include "dot11ah_channel.h"
|
|
++
|
|
++static const channel_to_halow_freq_t kNullAhValue = {0, 0, 0, 0};
|
|
++
|
|
++static const country_channel_map_t us_channel_map = {
|
|
++ .country = "US",
|
|
++ .num_mapped_channels = 48,
|
|
++ .ah_vals = {
|
|
++ /* 1Mhz */
|
|
++ {132, 1, 902.5, 0},
|
|
++ {136, 3, 903.5, 0},
|
|
++ {36, 5, 904.5, 0},
|
|
++ {40, 7, 905.5, 0},
|
|
++ {44, 9, 906.5, 0},
|
|
++ {48, 11, 907.5, 0},
|
|
++ {52, 13, 908.5, 0},
|
|
++ {56, 15, 909.5, 0},
|
|
++ {60, 17, 910.5, 0},
|
|
++ {64, 19, 911.5, 0},
|
|
++ {100, 21, 912.5, 0},
|
|
++ {104, 23, 913.5, 0},
|
|
++ {108, 25, 914.5, 0},
|
|
++ {112, 27, 915.5, 0},
|
|
++ {116, 29, 916.5, 0},
|
|
++ {120, 31, 917.5, 0},
|
|
++ {124, 33, 918.5, 0},
|
|
++ {128, 35, 919.5, 0},
|
|
++ {149, 37, 920.5, 0},
|
|
++ {153, 39, 921.5, 0},
|
|
++ {157, 41, 922.5, 0},
|
|
++ {161, 43, 923.5, 0},
|
|
++ {165, 45, 924.5, 0},
|
|
++ {169, 47, 925.5, 0},
|
|
++ {173, 49, 926.5, 0},
|
|
++ {177, 51, 927.5, 0},
|
|
++ /* 2MHz */
|
|
++ {134, 2, 903, 1},
|
|
++ {38, 6, 905, 1},
|
|
++ {46, 10, 907, 1},
|
|
++ {54, 14, 909, 1},
|
|
++ {62, 18, 911, 1},
|
|
++ {102, 22, 913, 1},
|
|
++ {110, 26, 915, 1},
|
|
++ {118, 30, 917, 1},
|
|
++ {126, 34, 919, 1},
|
|
++ {151, 38, 921, 1},
|
|
++ {159, 42, 923, 1},
|
|
++ {167, 46, 925, 1},
|
|
++ {175, 50, 927, 1},
|
|
++ /* 4MHz */
|
|
++ {42, 8, 906, 2},
|
|
++ {58, 16, 910, 2},
|
|
++ {106, 24, 914, 2},
|
|
++ {122, 32, 918, 2},
|
|
++ {155, 40, 922, 2},
|
|
++ {171, 48, 926, 2},
|
|
++ /* 8MHz */
|
|
++ {50, 12, 908, 3},
|
|
++ {114, 28, 916, 3},
|
|
++ {163, 44, 924, 3},
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t au_channel_map = {
|
|
++ .country = "AU",
|
|
++ .num_mapped_channels = 23,
|
|
++ .ah_vals = {
|
|
++ /* 1Mhz */
|
|
++ {112, 27, 915.5, 0},
|
|
++ {116, 29, 916.5, 0},
|
|
++ {120, 31, 917.5, 0},
|
|
++ {124, 33, 918.5, 0},
|
|
++ {128, 35, 919.5, 0},
|
|
++ {149, 37, 920.5, 0},
|
|
++ {153, 39, 921.5, 0},
|
|
++ {157, 41, 922.5, 0},
|
|
++ {161, 43, 923.5, 0},
|
|
++ {165, 45, 924.5, 0},
|
|
++ {169, 47, 925.5, 0},
|
|
++ {173, 49, 926.5, 0},
|
|
++ {177, 51, 927.5, 0},
|
|
++ /* 2Mhz */
|
|
++ {118, 30, 917, 1},
|
|
++ {126, 34, 919, 1},
|
|
++ {151, 38, 921, 1},
|
|
++ {159, 42, 923, 1},
|
|
++ {167, 46, 925, 1},
|
|
++ {175, 50, 927, 1},
|
|
++ /* 4 Mhz */
|
|
++ {122, 32, 918, 2},
|
|
++ {155, 40, 922, 2},
|
|
++ {171, 48, 926, 2},
|
|
++ /* 8 Mhz */
|
|
++ {163, 44, 924, 3}
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t nz_channel_map = {
|
|
++ .country = "NZ",
|
|
++ .num_mapped_channels = 23,
|
|
++ .ah_vals = {
|
|
++ /* 1Mhz */
|
|
++ {112, 27, 915.5, 0},
|
|
++ {116, 29, 916.5, 0},
|
|
++ {120, 31, 917.5, 0},
|
|
++ {124, 33, 918.5, 0},
|
|
++ {128, 35, 919.5, 0},
|
|
++ {149, 37, 920.5, 0},
|
|
++ {153, 39, 921.5, 0},
|
|
++ {157, 41, 922.5, 0},
|
|
++ {161, 43, 923.5, 0},
|
|
++ {165, 45, 924.5, 0},
|
|
++ {169, 47, 925.5, 0},
|
|
++ {173, 49, 926.5, 0},
|
|
++ {177, 51, 927.5, 0},
|
|
++ /* 2Mhz */
|
|
++ {118, 30, 917, 1},
|
|
++ {126, 34, 919, 1},
|
|
++ {151, 38, 921, 1},
|
|
++ {159, 42, 923, 1},
|
|
++ {167, 46, 925, 1},
|
|
++ {175, 50, 927, 1},
|
|
++ /* 4 Mhz */
|
|
++ {122, 32, 918, 2},
|
|
++ {155, 40, 922, 2},
|
|
++ {171, 48, 926, 2},
|
|
++ /* 8 Mhz */
|
|
++ {163, 44, 924, 3}
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t eu_channel_map = {
|
|
++ .country = "EU",
|
|
++ .num_mapped_channels = 8,
|
|
++ .ah_vals = {
|
|
++ /* 1Mhz */
|
|
++ {132, 1, 863.5, 0},
|
|
++ {136, 3, 864.5, 0},
|
|
++ {36, 5, 865.5, 0},
|
|
++ {40, 7, 866.5, 0},
|
|
++ {44, 9, 867.5, 0},
|
|
++ {120, 31, 916.9, 0},
|
|
++ {124, 33, 917.9, 0},
|
|
++ {128, 35, 918.9, 0},
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t in_channel_map = {
|
|
++ .country = "IN",
|
|
++ .num_mapped_channels = 3,
|
|
++ .ah_vals = {
|
|
++ /* 1Mhz */
|
|
++ {36, 5, 865.5, 0},
|
|
++ {40, 7, 866.5, 0},
|
|
++ {44, 9, 867.5, 0},
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t jp_channel_map = {
|
|
++ .country = "JP",
|
|
++ .num_mapped_channels = 11,
|
|
++ .ah_vals = {
|
|
++ /* 1 Mhz */
|
|
++ {36, 13, 923, 0},
|
|
++ {40, 15, 924, 0},
|
|
++ {44, 17, 925, 0},
|
|
++ {48, 19, 926, 0},
|
|
++ {64, 21, 927, 0},
|
|
++ /* 2Mhz */
|
|
++ {38, 2, 923.5, 1},
|
|
++ {46, 6, 925.5, 1},
|
|
++ {54, 4, 924.5, 1},
|
|
++ {62, 8, 926.5, 1},
|
|
++ /* 4Mhz */
|
|
++ {42, 36, 924.5, 2},
|
|
++ {58, 38, 925.5, 2},
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t kr_channel_map = {
|
|
++ .country = "KR",
|
|
++ .num_mapped_channels = 10,
|
|
++ .ah_vals = {
|
|
++ /* 1 Mhz */
|
|
++ {132, 1, 918, 0},
|
|
++ {136, 3, 919, 0},
|
|
++ {36, 5, 920, 0},
|
|
++ {40, 7, 921, 0},
|
|
++ {44, 9, 922, 0},
|
|
++ {48, 11, 923, 0},
|
|
++ /* 2Mhz */
|
|
++ {134, 2, 918.5, 1},
|
|
++ {38, 6, 920.5, 1},
|
|
++ {46, 10, 922.5, 1},
|
|
++ /* 4Mhz */
|
|
++ {42, 8, 921.5, 2},
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t sg_channel_map = {
|
|
++ .country = "SG",
|
|
++ .num_mapped_channels = 12,
|
|
++ .ah_vals = {
|
|
++ /* 1 Mhz */
|
|
++ {40, 7, 866.5, 0},
|
|
++ {44, 9, 867.5, 0},
|
|
++ {48, 11, 868.5, 0},
|
|
++ {149, 37, 920.5, 0},
|
|
++ {153, 39, 921.5, 0},
|
|
++ {157, 41, 922.5, 0},
|
|
++ {161, 43, 923.5, 0},
|
|
++ {165, 45, 924.5, 0},
|
|
++ /* 2Mhz */
|
|
++ {46, 10, 868, 1},
|
|
++ {151, 38, 921, 1},
|
|
++ {159, 42, 923, 1},
|
|
++ /* 4Mhz */
|
|
++ {155, 40, 922, 2}
|
|
++ }
|
|
++};
|
|
++
|
|
++static const country_channel_map_t channel_map_terminate = {
|
|
++ .country = {0,0,0},
|
|
++ .num_mapped_channels = 0,
|
|
++ .ah_vals = {}
|
|
++};
|
|
++
|
|
++static const country_channel_map_t *mapped_channel[] = {
|
|
++ &us_channel_map,
|
|
++ &au_channel_map,
|
|
++ &nz_channel_map,
|
|
++ &eu_channel_map,
|
|
++ &in_channel_map,
|
|
++ &jp_channel_map,
|
|
++ &kr_channel_map,
|
|
++ &sg_channel_map,
|
|
++ &channel_map_terminate
|
|
++};
|
|
++
|
|
++#define CHANNEL_MAP_SIZE (sizeof(mapped_channel) / sizeof(*mapped_channel))
|
|
++
|
|
++static void morse_get_country(country_channel_map_t *halow_vals)
|
|
++{
|
|
++ FILE *country_parameter;
|
|
++
|
|
++ country_parameter = fopen("/sys/module/morse/parameters/country", "r");
|
|
++ fscanf(country_parameter, "%2s", halow_vals->country);
|
|
++ fclose(country_parameter);
|
|
++}
|
|
++
|
|
++country_channel_map_t *set_s1g_channel_map(void)
|
|
++{
|
|
++ country_channel_map_t halow_vals;
|
|
++
|
|
++ morse_get_country(&halow_vals);
|
|
++ if (strlen(halow_vals.country) != 0)
|
|
++ {
|
|
++ for (int i = 0; i < CHANNEL_MAP_SIZE; i++)
|
|
++ {
|
|
++ if (!strncmp(halow_vals.country, mapped_channel[i]->country, strlen(mapped_channel[i]->country)))
|
|
++ {
|
|
++ return mapped_channel[i];
|
|
++ }
|
|
++ }
|
|
++ }
|
|
++
|
|
++ return NULL;
|
|
++}
|
|
++
|
|
++
|
|
++channel_to_halow_freq_t *get_s1g(country_channel_map_t *map, int channel)
|
|
++{
|
|
++ if(map == NULL)
|
|
++ return &kNullAhValue;
|
|
++
|
|
++ for(int i=0;i<map->num_mapped_channels;i++)
|
|
++ {
|
|
++ if(map->ah_vals[i].channel==channel)
|
|
++ return &map->ah_vals[i];
|
|
++ }
|
|
++ return &kNullAhValue;
|
|
++}
|
|
++
|
|
++float get_freq(country_channel_map_t *map, int channel)
|
|
++{
|
|
++ if(map == NULL)
|
|
++ return 0;
|
|
++
|
|
++ for(int i=0; i< map->num_mapped_channels; i++)
|
|
++ {
|
|
++ if(map->ah_vals[i].halow_channel==channel)
|
|
++ return map->ah_vals[i].halow_freq;
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++
|
|
++}
|
|
++
|
|
++int s1g_rate(int fiveG_rate, int frq_mhz)
|
|
++{
|
|
++ int sc_map_5g[][2] = {
|
|
++ {20 , 52},
|
|
++ {40 , 108},
|
|
++ {80 , 234},
|
|
++ {160 , 468}};
|
|
++ int sc_map_s1g[][2] = {
|
|
++ {20 , 24},
|
|
++ {40 , 52},
|
|
++ {80 , 108},
|
|
++ {160 , 234}};
|
|
++
|
|
++ int index=-1;
|
|
++ for (int i = 0; i < sizeof(sc_map_5g) / sizeof(int[2]); i++)
|
|
++ {
|
|
++ if(sc_map_5g[i][0] == frq_mhz)
|
|
++ {
|
|
++ index = i;
|
|
++ break;
|
|
++ }
|
|
++ }
|
|
++ int scale = 20; // for s1g we need to scale the reported shim layer values. if not exist approximate.
|
|
++ if (index != -1)
|
|
++ {
|
|
++ scale = 10 * sc_map_5g[index][1] / sc_map_s1g[index][1];
|
|
++ }
|
|
++ return fiveG_rate / scale;
|
|
++}
|
|
++
|
|
++int s1g_freq2channel(country_channel_map_t *map,int freq)//frq in khz
|
|
++{
|
|
++ if(map == NULL)
|
|
++ return 0;
|
|
++
|
|
++ for(int i=0; i< map->num_mapped_channels; i++)
|
|
++ {
|
|
++ if ((int)(map->ah_vals[i].halow_freq * 1000) == freq)
|
|
++ return map->ah_vals[i].halow_channel;
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++int s1g_chan2bw(country_channel_map_t *map,int channel)//bw in MHz
|
|
++{
|
|
++ if(map == NULL)
|
|
++ return 0;
|
|
++
|
|
++ for(int i=0; i< map->num_mapped_channels; i++)
|
|
++ {
|
|
++ if ((map->ah_vals[i].halow_channel) == channel)
|
|
++ return map->ah_vals[i].bw;
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++const country_channel_map_t** s1g_mapped_channel()
|
|
++{
|
|
++ return mapped_channel;
|
|
++}
|
|
++
|
|
++void s1g_get_country(char *buf)
|
|
++{
|
|
++ country_channel_map_t halow_vals;
|
|
++ morse_get_country(&halow_vals);
|
|
++ memcpy(buf,halow_vals.country,2);
|
|
++}
|
|
++
|
|
++
|
|
++// returns true if this raw is the selected rate.
|
|
++int mmrc_table_active_raw(const char* line)
|
|
++{
|
|
++ //check if it has MHz and MCS and GI keywords.
|
|
++ if (strstr(line, "MCS") == NULL)
|
|
++ return 0;
|
|
++ if (strstr(line, "MHz") == NULL)
|
|
++ return 0;
|
|
++ if ((strstr(line, "SGI") == NULL) && (strstr(line, "LGI") == NULL))
|
|
++ return 0;
|
|
++ //start search from 17th character.
|
|
++ if (strstr(line+17,"A"))
|
|
++ return 1;
|
|
++return 0;
|
|
++
|
|
++}
|
|
++//returns the avg tp from the selected line.
|
|
++int get_mmrc_table_raw_throughput_avg(const char* line)
|
|
++{
|
|
++ float tp_avg;
|
|
++ sscanf(line + 55, "%f", &tp_avg);
|
|
++ return tp_avg*1000;
|
|
++}
|
|
++
|
|
++int get_mmrc_throughput(const char* phyname)
|
|
++{
|
|
++ FILE *file;
|
|
++ char * line = NULL;
|
|
++ size_t len = 0;
|
|
++ ssize_t read;
|
|
++ char table_path[64];
|
|
++ int rate_kbps=-1;
|
|
++
|
|
++ sprintf (table_path,"/sys/kernel/debug/ieee80211/%s/morse/mmrc_table",phyname);
|
|
++ file = fopen(table_path, "r");
|
|
++ if (file == NULL)
|
|
++ {
|
|
++ return -1;
|
|
++ }
|
|
++
|
|
++ while ((read = getline(&line, &len, file)) != -1) {
|
|
++ if(mmrc_table_active_raw(line))
|
|
++ {
|
|
++ rate_kbps = get_mmrc_table_raw_throughput_avg(line);
|
|
++ break;
|
|
++ }
|
|
++ }
|
|
++ fclose(file);
|
|
++ if (line)
|
|
++ free(line);
|
|
++
|
|
++ if(rate_kbps == 0)
|
|
++ rate_kbps+=1; //to make sure that assoc list doesn't show "unknown" when there's no traffic.
|
|
++ return rate_kbps;
|
|
++}
|
|
+\ No newline at end of file
|
|
+diff --git a/dot11ah_channel.h b/dot11ah_channel.h
|
|
+new file mode 100644
|
|
+index 0000000..676906d
|
|
+--- /dev/null
|
|
++++ b/dot11ah_channel.h
|
|
+@@ -0,0 +1,105 @@
|
|
++/*
|
|
++ * Copyright 2022 Morse Micro
|
|
++ *
|
|
++ * The iwinfo library 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.
|
|
++ *
|
|
++ * The iwinfo library 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 the iwinfo library. If not, see http://www.gnu.org/licenses/.
|
|
++*/
|
|
++
|
|
++#ifndef __DOT11AH_CHANNELS__
|
|
++#define __DOT11AH_CHANNELS__
|
|
++
|
|
++typedef struct {
|
|
++ /*5G channel*/
|
|
++ int channel;
|
|
++ /*80211ah channel*/
|
|
++ int halow_channel;
|
|
++ /*80211ah freq*/
|
|
++ float halow_freq;
|
|
++ /*80211ah bandwidth*/
|
|
++ int bw;
|
|
++} channel_to_halow_freq_t;
|
|
++
|
|
++typedef struct {
|
|
++ char country[3];
|
|
++ int num_mapped_channels;
|
|
++ channel_to_halow_freq_t ah_vals[];
|
|
++} country_channel_map_t;
|
|
++
|
|
++
|
|
++/**
|
|
++ * @brief gets the channel 5g to s1g channel map for a given region.
|
|
++ * country setting is read from /sys/module/morse/parameters/country
|
|
++ */
|
|
++country_channel_map_t *set_s1g_channel_map(void);
|
|
++
|
|
++
|
|
++/**
|
|
++ * retrieves the map entry for a given 5g channel
|
|
++ * @param map a map of 5g to s1g channels retrieved by set_s1g_channel_map
|
|
++ * @param channel a 5g channel value
|
|
++ * @return The entry in the map corresponding to the given 5g channel and configured region
|
|
++ */
|
|
++channel_to_halow_freq_t *get_s1g(country_channel_map_t *map, int channel);
|
|
++
|
|
++/**
|
|
++ * retrieves the s1g frequency for a given s1g channel
|
|
++ * @param map a map of 5g to s1g channels retrieved by set_s1g_channel_map
|
|
++ * @param channel an s1g channel value
|
|
++ * @return a floating point value corresponding to the s1g frequency, in mhz, for the given channel.
|
|
++ */
|
|
++float get_freq(country_channel_map_t *map, int channel);
|
|
++
|
|
++/**
|
|
++ * a conversion helper for calculating the correct s1g mcs data rate for a given bandwidth and existing 5g mcs rate
|
|
++ * @param fiveG_rate a 5g mcs data rate value
|
|
++ * @param frq_mhz the 5g bandwidth corresponding to the 5g rate
|
|
++ * @return the s1g mcs data rate
|
|
++ */
|
|
++int s1g_rate(int fiveG_rate, int frq_mhz);
|
|
++
|
|
++/**
|
|
++ * converts s1g frequency to s1g channel
|
|
++ * @param map a map of 5g to s1g channels retrieved by set_s1g_channel_map
|
|
++ * @param freq s1g frequency in KHz
|
|
++ * @return s1g channel
|
|
++ */
|
|
++int s1g_freq2channel(country_channel_map_t *map,int freq);
|
|
++
|
|
++/**
|
|
++ * converts s1g channel to s1g bandwidth
|
|
++ * @param map a map of 5g to s1g channels retrieved by set_s1g_channel_map
|
|
++ * @param channel s1g channel
|
|
++ * @return s1g channel
|
|
++ */
|
|
++int s1g_chan2bw(country_channel_map_t *map,int channel);
|
|
++
|
|
++/**
|
|
++ * gives a pointer to the country separated channels map
|
|
++ * @return a pointer to the country separated channels map.
|
|
++ */
|
|
++const country_channel_map_t** s1g_mapped_channel();
|
|
++
|
|
++
|
|
++/**
|
|
++ * returns the currently set country.
|
|
++ * @param buf a pointer to the result buffer.
|
|
++ */
|
|
++void s1g_get_country(char *buf);
|
|
++
|
|
++/**
|
|
++ * gets mmrc average throughput from the mmrc_table.
|
|
++ * @param phyname phyname of the halow device.
|
|
++ * @return average throughput in kbps
|
|
++ */
|
|
++int get_mmrc_throughput(const char* phyname);
|
|
++
|
|
++#endif /* __DOT11AH_CHANNELS__ */
|
|
+diff --git a/include/iwinfo.h b/include/iwinfo.h
|
|
+index b50de69..0a9f157 100644
|
|
+--- a/include/iwinfo.h
|
|
++++ b/include/iwinfo.h
|
|
+@@ -31,6 +31,7 @@ enum iwinfo_80211 {
|
|
+ IWINFO_80211_AC,
|
|
+ IWINFO_80211_AD,
|
|
+ IWINFO_80211_AX,
|
|
++ IWINFO_80211_AH,
|
|
+
|
|
+ /* keep last */
|
|
+ IWINFO_80211_COUNT
|
|
+@@ -43,6 +44,9 @@ enum iwinfo_80211 {
|
|
+ #define IWINFO_80211_AC (1 << IWINFO_80211_AC)
|
|
+ #define IWINFO_80211_AD (1 << IWINFO_80211_AD)
|
|
+ #define IWINFO_80211_AX (1 << IWINFO_80211_AX)
|
|
++#define IWINFO_80211_AH (1 << IWINFO_80211_AH)
|
|
++
|
|
++#define S1G_CHAN_WIDTH_OFFSET (2)
|
|
+
|
|
+ extern const char * const IWINFO_80211_NAMES[IWINFO_80211_COUNT];
|
|
+
|
|
+@@ -52,7 +56,7 @@ enum iwinfo_band {
|
|
+ IWINFO_BAND_5,
|
|
+ IWINFO_BAND_6,
|
|
+ IWINFO_BAND_60,
|
|
+-
|
|
++ IWINFO_BAND_900,
|
|
+ /* keep last */
|
|
+ IWINFO_BAND_COUNT
|
|
+ };
|
|
+@@ -61,6 +65,7 @@ enum iwinfo_band {
|
|
+ #define IWINFO_BAND_5 (1 << IWINFO_BAND_5)
|
|
+ #define IWINFO_BAND_6 (1 << IWINFO_BAND_6)
|
|
+ #define IWINFO_BAND_60 (1 << IWINFO_BAND_60)
|
|
++#define IWINFO_BAND_900 (1 << IWINFO_BAND_900)
|
|
+
|
|
+ extern const char * const IWINFO_BAND_NAMES[IWINFO_BAND_COUNT];
|
|
+
|
|
+@@ -290,6 +295,15 @@ struct iwinfo_crypto_entry {
|
|
+ uint16_t pair_ciphers;
|
|
+ uint8_t auth_suites;
|
|
+ uint8_t auth_algs;
|
|
++
|
|
++ /* RSNXE data */
|
|
++ uint8_t prot_twt:1;
|
|
++ uint8_t sae_h2e:1;
|
|
++ uint8_t sae_pk:1;
|
|
++ uint8_t secure_ltf:1;
|
|
++ uint8_t secure_rtt:1;
|
|
++ uint8_t prot_range_neg:1;
|
|
++ uint8_t pad0:2;
|
|
+ };
|
|
+
|
|
+ struct iwinfo_scanlist_ht_chan_entry {
|
|
+@@ -304,6 +318,19 @@ struct iwinfo_scanlist_vht_chan_entry {
|
|
+ uint8_t center_chan_2;
|
|
+ };
|
|
+
|
|
++struct iwinfo_scanlist_ah_chan_entry {
|
|
++ uint8_t primary_chan;
|
|
++ uint8_t chan_width;
|
|
++};
|
|
++
|
|
++static uint16_t ah_chan_width[] = {
|
|
++ 1, /* 1 MHz*/
|
|
++ 2, /* 2 MHz*/
|
|
++ 4, /* 4 MHz*/
|
|
++ 8, /* 8 MHz*/
|
|
++ 16, /* 16 MHz*/
|
|
++};
|
|
++
|
|
+ extern const char * const ht_secondary_offset[4];
|
|
+ /* 0 = 20 MHz
|
|
+ 1 = 40 MHz or higher (refer to vht if supported) */
|
|
+@@ -327,6 +354,7 @@ struct iwinfo_scanlist_entry {
|
|
+ struct iwinfo_crypto_entry crypto;
|
|
+ struct iwinfo_scanlist_ht_chan_entry ht_chan_info;
|
|
+ struct iwinfo_scanlist_vht_chan_entry vht_chan_info;
|
|
++ struct iwinfo_scanlist_ah_chan_entry ah_chan_info;
|
|
+ };
|
|
+
|
|
+ struct iwinfo_country_entry {
|
|
+@@ -412,6 +440,7 @@ extern const struct iwinfo_ops wext_ops;
|
|
+ extern const struct iwinfo_ops madwifi_ops;
|
|
+ extern const struct iwinfo_ops nl80211_ops;
|
|
+ extern const struct iwinfo_ops wl_ops;
|
|
++extern const struct iwinfo_ops dot11ah_ops;
|
|
+
|
|
+ #include "iwinfo/utils.h"
|
|
+
|
|
+diff --git a/include/iwinfo/utils.h b/include/iwinfo/utils.h
|
|
+index 7b8ceea..d2a2788 100644
|
|
+--- a/include/iwinfo/utils.h
|
|
++++ b/include/iwinfo/utils.h
|
|
+@@ -66,6 +66,7 @@ int iwinfo_hardware_id_from_mtd(struct iwinfo_hardware_id *id);
|
|
+
|
|
+ void iwinfo_parse_rsn(struct iwinfo_crypto_entry *c, uint8_t *data, uint8_t len,
|
|
+ uint16_t defcipher, uint8_t defauth);
|
|
++void iwinfo_parse_rsnxe(struct iwinfo_crypto_entry *c, uint8_t *data, uint8_t len);
|
|
+
|
|
+ struct uci_section *iwinfo_uci_get_radio(const char *name, const char *type);
|
|
+ void iwinfo_uci_free(void);
|
|
+diff --git a/iwinfo_cli.c b/iwinfo_cli.c
|
|
+index 5dcee9a..790803c 100644
|
|
+--- a/iwinfo_cli.c
|
|
++++ b/iwinfo_cli.c
|
|
+@@ -69,12 +69,12 @@ static char * format_channel(int ch)
|
|
+
|
|
+ static char * format_frequency(int freq)
|
|
+ {
|
|
+- static char buf[11];
|
|
++ static char buf[15];
|
|
+
|
|
+ if (freq <= 0)
|
|
+ snprintf(buf, sizeof(buf), "unknown");
|
|
+ else
|
|
+- snprintf(buf, sizeof(buf), "%.3f GHz", ((float)freq / 1000.0));
|
|
++ snprintf(buf, sizeof(buf), "%.3f %s", ((float)freq / 1000.0), freq > 500000 ? "MHz" : "GHz");
|
|
+
|
|
+ return buf;
|
|
+ }
|
|
+@@ -351,6 +351,19 @@ static const char* format_chan_width(bool vht, uint8_t width)
|
|
+ return "unknown";
|
|
+ }
|
|
+
|
|
++static const char* format_ah_chan_width(uint8_t width)
|
|
++{
|
|
++ if (width < ARRAY_SIZE(ah_chan_width))
|
|
++ switch (ah_chan_width[width]) {
|
|
++ case 1: return "1 MHz";
|
|
++ case 2: return "2 MHz";
|
|
++ case 4: return "4 MHz";
|
|
++ case 8: return "8 MHz";
|
|
++ case 16: return "16 MHz";
|
|
++ }
|
|
++
|
|
++ return "unknown";
|
|
++}
|
|
+
|
|
+ static const char * print_type(const struct iwinfo_ops *iw, const char *ifname)
|
|
+ {
|
|
+@@ -689,13 +702,16 @@ static void print_scanlist(const struct iwinfo_ops *iw, const char *ifname)
|
|
+ format_quality_max(e->quality_max));
|
|
+ printf(" Encryption: %s\n",
|
|
+ format_encryption(&e->crypto));
|
|
+- printf(" HT Operation:\n");
|
|
+- printf(" Primary Channel: %d\n",
|
|
+- e->ht_chan_info.primary_chan);
|
|
+- printf(" Secondary Channel Offset: %s\n",
|
|
+- ht_secondary_offset[e->ht_chan_info.secondary_chan_off]);
|
|
+- printf(" Channel Width: %s\n",
|
|
+- format_chan_width(false, e->ht_chan_info.chan_width));
|
|
++
|
|
++ if (e->ht_chan_info.primary_chan) {
|
|
++ printf(" HT Operation:\n");
|
|
++ printf(" Primary Channel: %d\n",
|
|
++ e->ht_chan_info.primary_chan);
|
|
++ printf(" Secondary Channel Offset: %s\n",
|
|
++ ht_secondary_offset[e->ht_chan_info.secondary_chan_off]);
|
|
++ printf(" Channel Width: %s\n",
|
|
++ format_chan_width(false, e->ht_chan_info.chan_width));
|
|
++ }
|
|
+
|
|
+ if (e->vht_chan_info.center_chan_1) {
|
|
+ printf(" VHT Operation:\n");
|
|
+@@ -707,6 +723,14 @@ static void print_scanlist(const struct iwinfo_ops *iw, const char *ifname)
|
|
+ format_chan_width(true, e->vht_chan_info.chan_width));
|
|
+ }
|
|
+
|
|
++ if (e->ah_chan_info.primary_chan) {
|
|
++ printf(" AH Operation:\n");
|
|
++ printf(" Channel Width: %s\n",
|
|
++ format_ah_chan_width(e->ah_chan_info.chan_width));
|
|
++ printf(" Primary Channel: %d\n",
|
|
++ e->ah_chan_info.primary_chan);
|
|
++ }
|
|
++
|
|
+ printf("\n");
|
|
+ }
|
|
+ }
|
|
+@@ -912,7 +936,7 @@ int main(int argc, char **argv)
|
|
+ char *p;
|
|
+ const struct iwinfo_ops *iw;
|
|
+ glob_t globbuf;
|
|
+-
|
|
++
|
|
+ if (argc > 1 && argc < 3)
|
|
+ {
|
|
+ fprintf(stderr,
|
|
+@@ -1031,7 +1055,6 @@ int main(int argc, char **argv)
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+-
|
|
+ iwinfo_finish();
|
|
+
|
|
+ return rv;
|
|
+diff --git a/iwinfo_lib.c b/iwinfo_lib.c
|
|
+index 579efc4..02fbd9c 100644
|
|
+--- a/iwinfo_lib.c
|
|
++++ b/iwinfo_lib.c
|
|
+@@ -30,6 +30,7 @@ const char * const IWINFO_80211_NAMES[IWINFO_80211_COUNT] = {
|
|
+ "ac",
|
|
+ "ad",
|
|
+ "ax",
|
|
++ "ah",
|
|
+ };
|
|
+
|
|
+ const char * const IWINFO_BAND_NAMES[IWINFO_BAND_COUNT] = {
|
|
+@@ -37,6 +38,7 @@ const char * const IWINFO_BAND_NAMES[IWINFO_BAND_COUNT] = {
|
|
+ "5 GHz",
|
|
+ "6 GHz",
|
|
+ "60 GHz",
|
|
++ "900 MHz",
|
|
+ };
|
|
+
|
|
+ const char * const IWINFO_CIPHER_NAMES[IWINFO_CIPHER_COUNT] = {
|
|
+@@ -383,6 +385,7 @@ const struct iwinfo_iso3166_label IWINFO_ISO3166_NAMES[] = {
|
|
+
|
|
+ static const struct iwinfo_ops *backends[] = {
|
|
+ #ifdef USE_NL80211
|
|
++ &dot11ah_ops,
|
|
+ &nl80211_ops,
|
|
+ #endif
|
|
+ #ifdef USE_MADWIFI
|
|
+diff --git a/iwinfo_lua.c b/iwinfo_lua.c
|
|
+index ecf257d..9def386 100644
|
|
+--- a/iwinfo_lua.c
|
|
++++ b/iwinfo_lua.c
|
|
+@@ -554,6 +554,9 @@ static int iwinfo_L_hwmodelist(lua_State *L, int (*func)(const char *, int *))
|
|
+ lua_pushboolean(L, hwmodes & IWINFO_80211_AX);
|
|
+ lua_setfield(L, -2, "ax");
|
|
+
|
|
++ lua_pushboolean(L, hwmodes & IWINFO_80211_AH);
|
|
++ lua_setfield(L, -2, "ah");
|
|
++
|
|
+ return 1;
|
|
+ }
|
|
+
|
|
+diff --git a/iwinfo_morsecli.c b/iwinfo_morsecli.c
|
|
+new file mode 100644
|
|
+index 0000000..7156bd8
|
|
+--- /dev/null
|
|
++++ b/iwinfo_morsecli.c
|
|
+@@ -0,0 +1,122 @@
|
|
++/*
|
|
++ * iwinfo - Wireless Information Library - morsecli interface
|
|
++ *
|
|
++ * Copyright (C) 2023 Morse Micro
|
|
++ *
|
|
++ * The iwinfo library 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.
|
|
++ *
|
|
++ * The iwinfo library 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 the iwinfo library. If not, see http://www.gnu.org/licenses/.
|
|
++ */
|
|
++
|
|
++#include <stdarg.h>
|
|
++#include <stdlib.h>
|
|
++#include <stdio.h>
|
|
++#include <string.h>
|
|
++#include <unistd.h>
|
|
++
|
|
++
|
|
++/* popen using exec (rather than like system()).
|
|
++ *
|
|
++ * Since I have an irrational dislike of using /bin/sh unnecessarily,
|
|
++ * and we're potentially calling this quite a lot :(
|
|
++ */
|
|
++static FILE *execvp_popen_read(const char *file, const char *const argv[])
|
|
++{
|
|
++ int pipefd[2];
|
|
++
|
|
++ if (pipe(pipefd)) return NULL;
|
|
++
|
|
++ switch (fork())
|
|
++ {
|
|
++ case 0: /* child */
|
|
++ close(pipefd[0]);
|
|
++ dup2(pipefd[1], STDOUT_FILENO);
|
|
++ freopen("/dev/null", "w", stderr); /* throw away stderr, to avoid noise in output */
|
|
++ execvp(file, (char * const*)argv);
|
|
++ exit(1);
|
|
++ break;
|
|
++ default: /* parent */
|
|
++ close(pipefd[1]);
|
|
++ return fdopen(pipefd[0], "r");
|
|
++ case -1:
|
|
++ close(pipefd[1]);
|
|
++ return NULL;
|
|
++ }
|
|
++}
|
|
++
|
|
++/* Query particular stats from morse_cli.
|
|
++ *
|
|
++ * Style of function echos nl80211_hostapd_query.
|
|
++ *
|
|
++ * This is the YAGNI version of this, since hopefully
|
|
++ * it will be replaced by netlink queries in time.
|
|
++ *
|
|
++ * I don't use morse_cli's filter, since it's hard to construct
|
|
++ * a regex covering all the possible options (and filter
|
|
++ * doesn't do anything smart and still queries all the stats,
|
|
++ * so we may as well not use the regex engine at all).
|
|
++ *
|
|
++ * Notably:
|
|
++ * - uses the plain text output rather than JSON
|
|
++ * (which means it can't handle nested stats)
|
|
++ * - only handles integer stats
|
|
++ */
|
|
++int __morse_cli_stats_query(const char *ifname, ...)
|
|
++{
|
|
++ va_list ap, ap_cur;
|
|
++ int *dest;
|
|
++ char *search, *key, *val, buf[128];
|
|
++ int found = 0;
|
|
++ FILE *fp;
|
|
++ /* For now, the only stat we want is noise,
|
|
++ * so restrict to PHY core.
|
|
++ */
|
|
++ const char *const argv[] = {"morse_cli", "-i", ifname, "stats", "-u", NULL};
|
|
++
|
|
++ fp = execvp_popen_read("morse_cli", argv);
|
|
++
|
|
++ if (!fp)
|
|
++ return 0;
|
|
++
|
|
++ va_start(ap, ifname);
|
|
++
|
|
++ /* iterate applicable lines and copy found values into dest buffers */
|
|
++ while (fgets(buf, sizeof(buf), fp))
|
|
++ {
|
|
++ key = strtok(buf, ":\n");
|
|
++ val = strtok(NULL, "\n");
|
|
++
|
|
++ if (!key || !val || !*key)
|
|
++ continue;
|
|
++
|
|
++ va_copy(ap_cur, ap);
|
|
++
|
|
++ while ((search = va_arg(ap_cur, char *)) != NULL)
|
|
++ {
|
|
++ dest = va_arg(ap_cur, int *);
|
|
++
|
|
++ if (!strcmp(search, key))
|
|
++ {
|
|
++ *dest = atoi(val);
|
|
++ found++;
|
|
++ break;
|
|
++ }
|
|
++ }
|
|
++
|
|
++ va_end(ap_cur);
|
|
++ }
|
|
++
|
|
++ va_end(ap);
|
|
++
|
|
++ fclose(fp);
|
|
++
|
|
++ return found;
|
|
++}
|
|
+\ No newline at end of file
|
|
+diff --git a/iwinfo_morsecli.h b/iwinfo_morsecli.h
|
|
+new file mode 100644
|
|
+index 0000000..320f433
|
|
+--- /dev/null
|
|
++++ b/iwinfo_morsecli.h
|
|
+@@ -0,0 +1,27 @@
|
|
++/*
|
|
++ * iwinfo - Wireless Information Library - morsecli headers
|
|
++ *
|
|
++ * Copyright (C) 2023 Morse Micro
|
|
++ *
|
|
++ * The iwinfo library 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.
|
|
++ *
|
|
++ * The iwinfo library 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 the iwinfo library. If not, see http://www.gnu.org/licenses/.
|
|
++ */
|
|
++
|
|
++#ifndef __IWINFO_MORSECLI_H_
|
|
++#define __IWINFO_MORSECLI_H_
|
|
++
|
|
++#define morse_cli_stats_query(ifname, ...) \
|
|
++ __morse_cli_stats_query(ifname, ##__VA_ARGS__, NULL)
|
|
++
|
|
++int __morse_cli_stats_query(const char *ifname, ...);
|
|
++
|
|
++#endif
|
|
+\ No newline at end of file
|
|
+diff --git a/iwinfo_nl80211.c b/iwinfo_nl80211.c
|
|
+index 2200249..dcb29b3 100644
|
|
+--- a/iwinfo_nl80211.c
|
|
++++ b/iwinfo_nl80211.c
|
|
+@@ -2,6 +2,7 @@
|
|
+ * iwinfo - Wireless Information Library - NL80211 Backend
|
|
+ *
|
|
+ * Copyright (C) 2010-2013 Jo-Philipp Wich <xm@subsignal.org>
|
|
++ * Copyright 2022 Morse Micro
|
|
+ *
|
|
+ * The iwinfo library is free software: you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License version 2
|
|
+@@ -30,6 +31,7 @@
|
|
+ #include <stdlib.h>
|
|
+
|
|
+ #include "iwinfo_nl80211.h"
|
|
++#include "iwinfo_morsecli.h"
|
|
+
|
|
+ #define min(x, y) ((x) < (y)) ? (x) : (y)
|
|
+
|
|
+@@ -408,8 +410,11 @@ static int nl80211_phy_idx_from_uci(const char *name)
|
|
+ int idx = -1;
|
|
+
|
|
+ s = iwinfo_uci_get_radio(name, "mac80211");
|
|
+- if (!s)
|
|
+- goto out;
|
|
++ if (!s){
|
|
++ s = iwinfo_uci_get_radio(name, "morse");
|
|
++ if(!s)
|
|
++ goto out;
|
|
++ }
|
|
+
|
|
+ opt = uci_lookup_option_string(uci_ctx, s, "path");
|
|
+ idx = nl80211_phy_idx_from_path(opt);
|
|
+@@ -1331,15 +1336,16 @@ static int nl80211_get_bssid(const char *ifname, char *buf)
|
|
+
|
|
+ res = nl80211_phy2ifname(ifname);
|
|
+
|
|
+- /* try to obtain mac address via NL80211_CMD_GET_INTERFACE */
|
|
+- nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
|
|
+- nl80211_get_macaddr_cb, &sb);
|
|
++ /* try to find bssid from scan dump results (for AP, this fails
|
|
++ * and it goes to NL80211_CMD_GET_INTERFACE)*/
|
|
++ nl80211_request(res ? res : ifname,
|
|
++ NL80211_CMD_GET_SCAN, NLM_F_DUMP,
|
|
++ nl80211_get_ssid_bssid_cb, &sb);
|
|
+
|
|
+- /* failed, try to find bssid from scan dump results */
|
|
++ /* failed, try to obtain mac address via NL80211_CMD_GET_INTERFACE */
|
|
+ if (sb.bssid[0] == 0)
|
|
+- nl80211_request(res ? res : ifname,
|
|
+- NL80211_CMD_GET_SCAN, NLM_F_DUMP,
|
|
+- nl80211_get_ssid_bssid_cb, &sb);
|
|
++ nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
|
|
++ nl80211_get_macaddr_cb, &sb);
|
|
+
|
|
+ /* failed, try to find mac from hostapd info */
|
|
+ if ((sb.bssid[0] == 0) &&
|
|
+@@ -1771,7 +1777,8 @@ static const struct {
|
|
+ { "EAP-SUITE-B-192", 4, IWINFO_KMGMT_8021x },
|
|
+ { "EAP-SUITE-B", 4, IWINFO_KMGMT_8021x },
|
|
+ { "EAP-SHA384", 4, IWINFO_KMGMT_8021x },
|
|
+- { "EAP-SHA256", 0, IWINFO_KMGMT_8021x },
|
|
++ /* SHA256 counts as WPA3 as long as pmf is enabled; we check this below. */
|
|
++ { "EAP-SHA256", 4, IWINFO_KMGMT_8021x },
|
|
+ { "PSK-SHA256", 0, IWINFO_KMGMT_PSK },
|
|
+ { "NONE", 0, IWINFO_KMGMT_NONE },
|
|
+ { "None", 0, IWINFO_KMGMT_NONE },
|
|
+@@ -1782,7 +1789,7 @@ static const struct {
|
|
+ };
|
|
+
|
|
+ static void parse_wpa_suites(const char *str, int defversion,
|
|
+- uint8_t *versions, uint8_t *suites)
|
|
++ uint8_t *versions, uint8_t *suites, int pmf)
|
|
+ {
|
|
+ size_t l;
|
|
+ int i, version;
|
|
+@@ -1817,6 +1824,35 @@ static void parse_wpa_suites(const char *str, int defversion,
|
|
+
|
|
+ p = q + strspn(q, sep);
|
|
+ }
|
|
++
|
|
++ /* Handle ieee80211w/pmf (management frame protection).
|
|
++ *
|
|
++ * Strictly:
|
|
++ * ieee80211w=2 && wpa_key_mgmt=WPA-EAP-SHA256
|
|
++ * => WPA3-Enterprise
|
|
++ * ieee80211w=1 && wpa_key_mgmt=WPA-EAP WPA-EAP-SHA256
|
|
++ * => WPA3-Enterprise transition
|
|
++ *
|
|
++ * Here we just try to aggressively downgrade (i.e. if no pmf,
|
|
++ * not WPA3-Enterprise, and if not required then WPA2/WPA3).
|
|
++ * This _will_ allow some invalid configurations through
|
|
++ * and count certain undefined configurations as WPA2/WPA3
|
|
++ * (e.g. WPA-EAP-SHA256 only and ieee80211=1).
|
|
++ */
|
|
++ if ((*suites & IWINFO_KMGMT_8021x) && (*versions & 4))
|
|
++ switch(pmf)
|
|
++ {
|
|
++ case 0: /* if disabled, it's not WPA3 */
|
|
++ *versions &= ~4;
|
|
++ *versions |= defversion;
|
|
++ break;
|
|
++ case 1: /* if not required, not only WPA3 */
|
|
++ *versions |= defversion;
|
|
++ break;
|
|
++ case 2: /* pmf required */
|
|
++ default: /* if no pmf info - e.g. from scan */
|
|
++ break;
|
|
++ }
|
|
+ }
|
|
+
|
|
+ static const struct {
|
|
+@@ -1871,6 +1907,7 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
|
|
+ uint8_t wpa_version = 0;
|
|
+ char wpa[2], wpa_key_mgmt[64], wpa_pairwise[16], wpa_groupwise[16];
|
|
+ char auth_algs[2], wep_key0[27], wep_key1[27], wep_key2[27], wep_key3[27];
|
|
++ char ieee80211w[2], pmf[2];
|
|
+ char mode[16];
|
|
+
|
|
+ struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
|
|
+@@ -1880,6 +1917,7 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
|
|
+ "pairwise_cipher", wpa_pairwise, sizeof(wpa_pairwise),
|
|
+ "group_cipher", wpa_groupwise, sizeof(wpa_groupwise),
|
|
+ "key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
|
|
++ "pmf", pmf, sizeof(pmf),
|
|
+ "mode", mode, sizeof(mode)))
|
|
+ {
|
|
+ /* WEP or Open */
|
|
+@@ -1928,7 +1966,7 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
|
|
+ wpa_version = 1;
|
|
+ }
|
|
+
|
|
+- parse_wpa_suites(p, wpa_version, &c->wpa_version, &c->auth_suites);
|
|
++ parse_wpa_suites(p, wpa_version, &c->wpa_version, &c->auth_suites, atoi(pmf));
|
|
+
|
|
+ c->enabled = !!(c->wpa_version && c->auth_suites);
|
|
+ }
|
|
+@@ -1941,6 +1979,7 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
|
|
+ "wpa", wpa, sizeof(wpa),
|
|
+ "wpa_key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
|
|
+ "wpa_pairwise", wpa_pairwise, sizeof(wpa_pairwise),
|
|
++ "ieee80211w", ieee80211w, sizeof(ieee80211w),
|
|
+ "auth_algs", auth_algs, sizeof(auth_algs),
|
|
+ "wep_key0", wep_key0, sizeof(wep_key0),
|
|
+ "wep_key1", wep_key1, sizeof(wep_key1),
|
|
+@@ -1959,7 +1998,7 @@ static int nl80211_get_encryption(const char *ifname, char *buf)
|
|
+ if (!strncmp(p, "FT-", 3))
|
|
+ p += 3;
|
|
+
|
|
+- parse_wpa_suites(p, atoi(wpa), &c->wpa_version, &c->auth_suites);
|
|
++ parse_wpa_suites(p, atoi(wpa), &c->wpa_version, &c->auth_suites, atoi(ieee80211w));
|
|
+ }
|
|
+
|
|
+ c->enabled = c->wpa_version ? 1 : 0;
|
|
+@@ -2531,7 +2570,7 @@ static void nl80211_get_scancrypto(char *spec, struct iwinfo_crypto_entry *c)
|
|
+
|
|
+ c->enabled = 1;
|
|
+
|
|
+- parse_wpa_suites(suites, wpa_version, &c->wpa_version, &c->auth_suites);
|
|
++ parse_wpa_suites(suites, wpa_version, &c->wpa_version, &c->auth_suites, -1);
|
|
+ parse_wpa_ciphers(suites, &c->pair_ciphers);
|
|
+ }
|
|
+ }
|
|
+@@ -2588,6 +2627,9 @@ static void nl80211_get_scanlist_ie(struct nlattr **bss,
|
|
+ e->vht_chan_info.center_chan_2 = ie[4];
|
|
+ }
|
|
+ break;
|
|
++ case 244: /* RSNXE */
|
|
++ iwinfo_parse_rsnxe(&e->crypto, ie + 2, ie[1]);
|
|
++ break;
|
|
+ }
|
|
+
|
|
+ ielen -= ie[1] + 2;
|
|
+@@ -3634,7 +3676,7 @@ const struct iwinfo_ops nl80211_ops = {
|
|
+ .mbssid_support = nl80211_get_mbssid_support,
|
|
+ .hwmodelist = nl80211_get_hwmodelist,
|
|
+ .htmodelist = nl80211_get_htmodelist,
|
|
+- .htmode = nl80211_get_htmode,
|
|
++ .htmode = nl80211_get_htmode,
|
|
+ .mode = nl80211_get_mode,
|
|
+ .ssid = nl80211_get_ssid,
|
|
+ .bssid = nl80211_get_bssid,
|
|
+@@ -3653,3 +3695,723 @@ const struct iwinfo_ops nl80211_ops = {
|
|
+ .phy_path = nl80211_phy_path,
|
|
+ .close = nl80211_close
|
|
+ };
|
|
++
|
|
++
|
|
++/*
|
|
++ * Morse Micro Shim Layer
|
|
++ *
|
|
++ */
|
|
++
|
|
++#include "dot11ah_channel.h"
|
|
++
|
|
++country_channel_map_t *g_map = NULL;
|
|
++
|
|
++static bool nl80211_is_halow(const char *ifname)
|
|
++{
|
|
++ const struct iwinfo_hardware_entry *e = nl80211_get_hardware_entry(ifname);
|
|
++ if (!e)
|
|
++ return false;
|
|
++
|
|
++ if (strcmp(e->device_name, "HaLow WiFi"))
|
|
++ return false;
|
|
++
|
|
++ return true;
|
|
++}
|
|
++
|
|
++static inline void _sanitise_rate_entry(struct iwinfo_rate_entry *re){
|
|
++ if(re == NULL)
|
|
++ return;
|
|
++ re->rate = s1g_rate(re->rate, re->mhz);
|
|
++ re->mhz /= 20;
|
|
++ re->is_vht = 0;
|
|
++ re->is_ht = 1;
|
|
++}
|
|
++
|
|
++
|
|
++/*
|
|
++ * These fill_signal handlers need to be modified for s1g as they work per station.
|
|
++ * And it's more correct to handle the per station values in the per station handler
|
|
++ */
|
|
++
|
|
++static uint8_t nl80211_get_bandwidth(struct nlattr **ri, uint32_t size){
|
|
++ if ((NL80211_RATE_INFO_5_MHZ_WIDTH < size)
|
|
++ && (ri[NL80211_RATE_INFO_5_MHZ_WIDTH]))
|
|
++ return 5;
|
|
++ else if ((NL80211_RATE_INFO_5_MHZ_WIDTH < size)
|
|
++ && (ri[NL80211_RATE_INFO_10_MHZ_WIDTH]))
|
|
++ return 10;
|
|
++ else if ((NL80211_RATE_INFO_5_MHZ_WIDTH < size)
|
|
++ && (ri[NL80211_RATE_INFO_40_MHZ_WIDTH]))
|
|
++ return 40;
|
|
++ else if ((NL80211_RATE_INFO_5_MHZ_WIDTH < size)
|
|
++ && (ri[NL80211_RATE_INFO_80_MHZ_WIDTH]))
|
|
++ return 80;
|
|
++ else if ((NL80211_RATE_INFO_5_MHZ_WIDTH < size)
|
|
++ && (ri[NL80211_RATE_INFO_80P80_MHZ_WIDTH] ||
|
|
++ ri[NL80211_RATE_INFO_160_MHZ_WIDTH]))
|
|
++ return 160;
|
|
++ else
|
|
++ return 20;
|
|
++}
|
|
++
|
|
++static int dot11ah_fill_signal_cb(struct nl_msg *msg, void *arg)
|
|
++{
|
|
++ int8_t dbm;
|
|
++ int16_t mbit;
|
|
++ struct nl80211_rssi_rate *rr = arg;
|
|
++ struct nlattr **attr = nl80211_parse(msg);
|
|
++ struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
|
|
++ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
|
|
++
|
|
++ static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
|
|
++ [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
|
|
++ [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
|
|
++ [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
|
|
++ [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
|
|
++ [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
|
|
++ [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
|
|
++ [NL80211_STA_INFO_TX_BITRATE] = { .type = NLA_NESTED },
|
|
++ [NL80211_STA_INFO_LLID] = { .type = NLA_U16 },
|
|
++ [NL80211_STA_INFO_PLID] = { .type = NLA_U16 },
|
|
++ [NL80211_STA_INFO_PLINK_STATE] = { .type = NLA_U8 },
|
|
++ };
|
|
++
|
|
++ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
|
|
++ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
|
|
++ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
|
|
++ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
|
|
++ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
|
|
++ };
|
|
++
|
|
++ if (attr[NL80211_ATTR_STA_INFO])
|
|
++ {
|
|
++ if (!nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
|
|
++ attr[NL80211_ATTR_STA_INFO], stats_policy))
|
|
++ {
|
|
++ if (sinfo[NL80211_STA_INFO_SIGNAL])
|
|
++ {
|
|
++ dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
|
|
++ rr->rssi = (rr->rssi * rr->rssi_samples + dbm) / (rr->rssi_samples + 1);
|
|
++ rr->rssi_samples++;
|
|
++ }
|
|
++
|
|
++ if (sinfo[NL80211_STA_INFO_TX_BITRATE])
|
|
++ {
|
|
++ if (!nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
|
|
++ sinfo[NL80211_STA_INFO_TX_BITRATE],
|
|
++ rate_policy))
|
|
++ {
|
|
++ if (rinfo[NL80211_RATE_INFO_BITRATE])
|
|
++ {
|
|
++ uint8_t mhz = nl80211_get_bandwidth(rinfo, NL80211_RATE_INFO_MAX);
|
|
++ mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
|
|
++ mbit = s1g_rate(mbit, mhz);
|
|
++ rr->rate = (rr->rate * rr->rate_samples + mbit) / (rr->rate_samples + 1);
|
|
++ rr->rate_samples++;
|
|
++ }
|
|
++ }
|
|
++ }
|
|
++ }
|
|
++ }
|
|
++
|
|
++ return NL_SKIP;
|
|
++}
|
|
++
|
|
++static void dot11ah_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
|
|
++{
|
|
++ DIR *d;
|
|
++ struct dirent *de;
|
|
++
|
|
++ memset(r, 0, sizeof(*r));
|
|
++
|
|
++ if ((d = opendir("/sys/class/net")) != NULL)
|
|
++ {
|
|
++ while ((de = readdir(d)) != NULL)
|
|
++ {
|
|
++ if (!strncmp(de->d_name, ifname, strlen(ifname)) &&
|
|
++ (!de->d_name[strlen(ifname)] ||
|
|
++ !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
|
|
++ {
|
|
++ nl80211_request(de->d_name, NL80211_CMD_GET_STATION,
|
|
++ NLM_F_DUMP, dot11ah_fill_signal_cb, r);
|
|
++ }
|
|
++ }
|
|
++
|
|
++ closedir(d);
|
|
++ }
|
|
++}
|
|
++
|
|
++static int dot11ah_probe(const char *ifname)
|
|
++{
|
|
++ if (!nl80211_ifname2phy(ifname))
|
|
++ return 0;
|
|
++
|
|
++ if (!nl80211_is_halow(ifname))
|
|
++ return 0;
|
|
++
|
|
++ g_map = set_s1g_channel_map();
|
|
++ return 1;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_center_chan2(const char *ifname, int *buf)
|
|
++{
|
|
++ if(nl80211_get_center_chan2(ifname, buf) < 0)
|
|
++ return -1;
|
|
++
|
|
++ channel_to_halow_freq_t *ch_entry = get_s1g(g_map, *buf);
|
|
++ if(ch_entry == NULL)
|
|
++ return -1;
|
|
++
|
|
++ *buf = ch_entry->halow_channel;
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_center_chan1(const char *ifname, int *buf)
|
|
++{
|
|
++ if(nl80211_get_center_chan1(ifname, buf) < 0)
|
|
++ return -1;
|
|
++
|
|
++ channel_to_halow_freq_t *ch_entry = get_s1g(g_map, *buf);
|
|
++ if(ch_entry == NULL)
|
|
++ return -1;
|
|
++
|
|
++ *buf = ch_entry->halow_channel;
|
|
++
|
|
++ return 0;
|
|
++
|
|
++}
|
|
++
|
|
++static int dot11ah_get_channel(const char *ifname, int *buf)
|
|
++{
|
|
++ if(dot11ah_get_center_chan1(ifname, buf) == 0)
|
|
++ return 0;
|
|
++
|
|
++ if(nl80211_get_channel(ifname, buf) < 0)
|
|
++ return -1;
|
|
++
|
|
++ channel_to_halow_freq_t *ch_entry = get_s1g(g_map, *buf);
|
|
++ if(ch_entry == NULL)
|
|
++ return -1;
|
|
++
|
|
++ *buf = ch_entry->halow_channel;
|
|
++
|
|
++ return 0;
|
|
++
|
|
++}
|
|
++
|
|
++static int dot11ah_get_frequency(const char *ifname, int *buf)
|
|
++{
|
|
++ *buf = 0;
|
|
++
|
|
++ if (dot11ah_get_center_chan1(ifname, buf) < 0)
|
|
++ if (dot11ah_get_channel(ifname, buf) < 0)
|
|
++ return -1;
|
|
++
|
|
++ *buf = (int) (get_freq(g_map, *buf)*1000);
|
|
++
|
|
++ return (*buf == 0) ? -1 : 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_bitrate(const char *ifname, int *buf)
|
|
++{
|
|
++ struct nl80211_rssi_rate rr;
|
|
++
|
|
++ dot11ah_fill_signal(ifname, &rr);
|
|
++
|
|
++ if (rr.rate_samples)
|
|
++ {
|
|
++ *buf = (rr.rate * 100);
|
|
++ return 0;
|
|
++ }
|
|
++
|
|
++ return -1;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_hwmodelist(const char *ifname, int *buf)
|
|
++{
|
|
++ *buf = IWINFO_80211_AH;
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_htmodelist(const char *ifname, int *buf)
|
|
++{
|
|
++ *buf = IWINFO_HTMODE_NOHT;
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_htmode(const char *ifname, int *buf)
|
|
++{
|
|
++ int chan;
|
|
++ dot11ah_get_channel(ifname, &chan);
|
|
++ if(!g_map || !chan)
|
|
++ return 0;
|
|
++ *buf = s1g_chan2bw(g_map, chan);
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_country(const char *ifname, char *buf)
|
|
++{
|
|
++ s1g_get_country(buf);
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_noise(const char *ifname, int *buf)
|
|
++{
|
|
++ if (!morse_cli_stats_query(ifname, "Noise dBm", buf))
|
|
++ return -1;
|
|
++ else
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_assoclist(const char *ifname, char *buf, int *len)
|
|
++{
|
|
++ struct iwinfo_assoclist_entry *ae;
|
|
++ int noise = 0;
|
|
++
|
|
++ if (nl80211_get_assoclist(ifname, buf, len) < 0)
|
|
++ return -1;
|
|
++
|
|
++ dot11ah_get_noise(ifname, &noise);
|
|
++
|
|
++ for (char *p = buf; p < (buf + *len); p += sizeof(struct iwinfo_assoclist_entry))
|
|
++ {
|
|
++ ae = (struct iwinfo_assoclist_entry *) p;
|
|
++ ae->noise = noise;
|
|
++ _sanitise_rate_entry(&ae->rx_rate);
|
|
++ _sanitise_rate_entry(&ae->tx_rate);
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_scanlist(const char *ifname, char *buf, int *len)
|
|
++{
|
|
++ struct iwinfo_scanlist_entry *se;
|
|
++ channel_to_halow_freq_t *ch_entry, *prim_chan;
|
|
++ if(nl80211_get_scanlist(ifname, buf, len) < 0)
|
|
++ return -1;
|
|
++
|
|
++ for(char *p = buf; p < (buf + *len); p += sizeof(struct iwinfo_scanlist_entry)){
|
|
++ se = (struct iwinfo_scanlist_entry *) p;
|
|
++
|
|
++ ch_entry = get_s1g(g_map, se->channel);
|
|
++ prim_chan = get_s1g(g_map, se->ht_chan_info.primary_chan);
|
|
++ se->channel = ch_entry->halow_channel;
|
|
++ se->band = IWINFO_BAND_900;
|
|
++ se->mhz = get_freq(g_map, se->channel)*1000;
|
|
++
|
|
++ if (se->vht_chan_info.center_chan_1)
|
|
++ {
|
|
++ ch_entry = get_s1g(g_map, se->vht_chan_info.center_chan_1);
|
|
++ se->channel = ch_entry->halow_channel;
|
|
++ se->vht_chan_info.center_chan_1 = 0;
|
|
++ }else if(se->ht_chan_info.secondary_chan_off == 1)
|
|
++ {
|
|
++ se->channel += 1;
|
|
++ }else if(se->ht_chan_info.secondary_chan_off == 3)
|
|
++ {
|
|
++ se->channel -= 1;
|
|
++ }
|
|
++ se->ht_chan_info.secondary_chan_off=0;
|
|
++ se->ht_chan_info.primary_chan=0;
|
|
++ se->ah_chan_info.primary_chan=prim_chan->halow_channel;
|
|
++ se->ah_chan_info.chan_width=s1g_chan2bw(g_map, se->channel);
|
|
++
|
|
++ se->crypto.wpa_version |= 4;
|
|
++ if(se->crypto.sae_h2e == 1)
|
|
++ se->crypto.auth_suites |= IWINFO_KMGMT_SAE;
|
|
++ else
|
|
++ se->crypto.auth_suites |= IWINFO_KMGMT_OWE;
|
|
++ se->crypto.group_ciphers |= IWINFO_CIPHER_CCMP;
|
|
++ se->crypto.group_ciphers &= ~(IWINFO_CIPHER_WEP40 | IWINFO_CIPHER_WEP104);
|
|
++ se->crypto.pair_ciphers |= IWINFO_CIPHER_CCMP;
|
|
++ se->crypto.pair_ciphers &= ~(IWINFO_CIPHER_WEP40 | IWINFO_CIPHER_WEP104);
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++int dot11ah_freq_compare(const void *a, const void *b)
|
|
++{
|
|
++ const struct iwinfo_freqlist_entry *fe1 = (const struct iwinfo_freqlist_entry *) a;
|
|
++ const struct iwinfo_freqlist_entry *fe2 = (const struct iwinfo_freqlist_entry *) b;
|
|
++ return ( fe1->mhz - fe2->mhz );
|
|
++}
|
|
++
|
|
++static int dot11ah_get_freqlist(const char *ifname, char *buf, int *len)
|
|
++{
|
|
++ struct iwinfo_freqlist_entry *fe;
|
|
++ channel_to_halow_freq_t *ch_entry;
|
|
++ const size_t fe_size = sizeof(struct iwinfo_freqlist_entry);
|
|
++ if(nl80211_get_freqlist(ifname, buf, len) < 0)
|
|
++ return -1;
|
|
++
|
|
++ for(char *p = buf; p < (buf + *len); p += fe_size){
|
|
++ fe = (struct iwinfo_freqlist_entry *) p;
|
|
++ ch_entry = get_s1g(g_map, fe->channel);
|
|
++ fe->channel = ch_entry->halow_channel;
|
|
++ fe->mhz = get_freq(g_map, fe->channel)*1000;
|
|
++ fe->band = IWINFO_BAND_900;
|
|
++ fe->flags = 0;
|
|
++ }
|
|
++
|
|
++ qsort(buf, *len/fe_size, fe_size, dot11ah_freq_compare);
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++static int dot11ah_get_countrylist(const char *ifname, char *buf, int *len)
|
|
++{
|
|
++ int count;
|
|
++ struct iwinfo_country_entry *e = (struct iwinfo_country_entry *)buf;
|
|
++
|
|
++ const country_channel_map_t **halow_map = s1g_mapped_channel();
|
|
++ char higher='0',lower='0';
|
|
++ e->iso3166 = (int)higher*256+lower;
|
|
++ e->ccode[0] = '0';
|
|
++ e->ccode[1] = '0';
|
|
++ e->ccode[2] = 0;
|
|
++ e++;
|
|
++ count=1;
|
|
++ while((*halow_map)->country[0])
|
|
++ {
|
|
++ higher = (*halow_map)->country[0];
|
|
++ lower = (*halow_map)->country[1];
|
|
++ e->iso3166 = higher * 256 + lower;
|
|
++ e->ccode[0] = higher;
|
|
++ e->ccode[1] = lower;
|
|
++ e->ccode[2] = 0;
|
|
++ e++;
|
|
++ halow_map++;
|
|
++ count++;
|
|
++ }
|
|
++ *len = (count * sizeof(struct iwinfo_country_entry));
|
|
++ return 0;
|
|
++}
|
|
++
|
|
++/*
|
|
++ * modified function to look for the interface in wpa_supplicant_s1g
|
|
++ */
|
|
++static int dot11ah_wpactl_connect(const char *ifname, struct sockaddr_un *local)
|
|
++{
|
|
++ struct sockaddr_un remote = { 0 };
|
|
++ size_t remote_length, local_length;
|
|
++
|
|
++ int sock = socket(PF_UNIX, SOCK_DGRAM, 0);
|
|
++ if (sock < 0)
|
|
++ return sock;
|
|
++
|
|
++ remote.sun_family = AF_UNIX;
|
|
++ remote_length = sizeof(remote.sun_family) +
|
|
++ sprintf(remote.sun_path, "/var/run/wpa_supplicant-%s/%s",
|
|
++ ifname, ifname);
|
|
++
|
|
++ if (fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC) < 0)
|
|
++ {
|
|
++ close(sock);
|
|
++ return -1;
|
|
++ }
|
|
++
|
|
++ if (connect(sock, (struct sockaddr *)&remote, remote_length))
|
|
++ {
|
|
++ remote_length = sizeof(remote.sun_family) +
|
|
++ sprintf(remote.sun_path, "/var/run/wpa_supplicant_s1g/%s", ifname);
|
|
++
|
|
++ if (connect(sock, (struct sockaddr *)&remote, remote_length))
|
|
++ {
|
|
++ close(sock);
|
|
++ return -1;
|
|
++ }
|
|
++ }
|
|
++
|
|
++ local->sun_family = AF_UNIX;
|
|
++ local_length = sizeof(local->sun_family) +
|
|
++ sprintf(local->sun_path, "/var/run/iwinfo-%s-%d", ifname, getpid());
|
|
++
|
|
++ if (bind(sock, (struct sockaddr *)local, local_length) < 0)
|
|
++ {
|
|
++ close(sock);
|
|
++ return -1;
|
|
++ }
|
|
++
|
|
++ return sock;
|
|
++}
|
|
++
|
|
++/* function copied as is from nl80211 version*/
|
|
++static int __dot11ah_wpactl_query(const char *ifname, ...)
|
|
++{
|
|
++ va_list ap, ap_cur;
|
|
++ struct sockaddr_un local = { 0 };
|
|
++ int len, mode, found = 0, sock = -1;
|
|
++ char *search, *dest, *key, *val, *line, *pos, buf[512];
|
|
++
|
|
++ if (nl80211_get_mode(ifname, &mode))
|
|
++ return 0;
|
|
++
|
|
++ if (mode != IWINFO_OPMODE_CLIENT &&
|
|
++ mode != IWINFO_OPMODE_ADHOC &&
|
|
++ mode != IWINFO_OPMODE_MESHPOINT)
|
|
++ return 0;
|
|
++
|
|
++ sock = dot11ah_wpactl_connect(ifname, &local);
|
|
++
|
|
++ if (sock < 0)
|
|
++ return 0;
|
|
++
|
|
++ va_start(ap, ifname);
|
|
++
|
|
++ /* clear all destination buffers */
|
|
++ va_copy(ap_cur, ap);
|
|
++
|
|
++ while ((search = va_arg(ap_cur, char *)) != NULL)
|
|
++ {
|
|
++ dest = va_arg(ap_cur, char *);
|
|
++ len = va_arg(ap_cur, int);
|
|
++
|
|
++ memset(dest, 0, len);
|
|
++ }
|
|
++
|
|
++ va_end(ap_cur);
|
|
++
|
|
++ send(sock, "STATUS", 6, 0);
|
|
++
|
|
++ while (true)
|
|
++ {
|
|
++ if (nl80211_wpactl_recv(sock, buf, sizeof(buf)) <= 0)
|
|
++ break;
|
|
++
|
|
++ if (buf[0] == '<')
|
|
++ continue;
|
|
++
|
|
++ for (line = strtok_r(buf, "\n", &pos);
|
|
++ line != NULL;
|
|
++ line = strtok_r(NULL, "\n", &pos))
|
|
++ {
|
|
++ key = strtok(line, "=");
|
|
++ val = strtok(NULL, "\n");
|
|
++
|
|
++ if (!key || !val)
|
|
++ continue;
|
|
++
|
|
++ va_copy(ap_cur, ap);
|
|
++
|
|
++ while ((search = va_arg(ap_cur, char *)) != NULL)
|
|
++ {
|
|
++ dest = va_arg(ap_cur, char *);
|
|
++ len = va_arg(ap_cur, int);
|
|
++
|
|
++ if (!strcmp(search, key))
|
|
++ {
|
|
++ strncpy(dest, val, len - 1);
|
|
++ found++;
|
|
++ break;
|
|
++ }
|
|
++ }
|
|
++
|
|
++ va_end(ap_cur);
|
|
++ }
|
|
++
|
|
++ break;
|
|
++ }
|
|
++
|
|
++ va_end(ap);
|
|
++
|
|
++ close(sock);
|
|
++ unlink(local.sun_path);
|
|
++
|
|
++ return found;
|
|
++}
|
|
++
|
|
++#define dot11ah_wpactl_query(ifname, ...) \
|
|
++ __dot11ah_wpactl_query(ifname, ##__VA_ARGS__, NULL)
|
|
++
|
|
++/* function copied as is from nl80211 version*/
|
|
++static int dot11ah_get_encryption(const char *ifname, char *buf)
|
|
++{
|
|
++ char *p;
|
|
++ int opmode;
|
|
++ uint8_t wpa_version = 0;
|
|
++ char wpa[2], wpa_key_mgmt[64], wpa_pairwise[16], wpa_groupwise[16];
|
|
++ char auth_algs[2], wep_key0[27], wep_key1[27], wep_key2[27], wep_key3[27];
|
|
++ char ieee80211w[2], pmf[2];
|
|
++ char mode[16];
|
|
++
|
|
++ struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
|
|
++
|
|
++ /* WPA supplicant */
|
|
++ if (dot11ah_wpactl_query(ifname,
|
|
++ "pairwise_cipher", wpa_pairwise, sizeof(wpa_pairwise),
|
|
++ "group_cipher", wpa_groupwise, sizeof(wpa_groupwise),
|
|
++ "key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
|
|
++ "pmf", pmf, sizeof(pmf),
|
|
++ "mode", mode, sizeof(mode)))
|
|
++ {
|
|
++ /* WEP or Open */
|
|
++ if (!strcmp(wpa_key_mgmt, "NONE"))
|
|
++ {
|
|
++ parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
|
|
++ parse_wpa_ciphers(wpa_groupwise, &c->group_ciphers);
|
|
++
|
|
++ if (c->pair_ciphers != 0 && c->pair_ciphers != IWINFO_CIPHER_NONE) {
|
|
++ c->enabled = 1;
|
|
++ c->auth_suites = IWINFO_KMGMT_NONE;
|
|
++ c->auth_algs = IWINFO_AUTH_OPEN | IWINFO_AUTH_SHARED;
|
|
++ }
|
|
++ else {
|
|
++ c->pair_ciphers = 0;
|
|
++ c->group_ciphers = 0;
|
|
++ }
|
|
++ }
|
|
++
|
|
++ /* MESH with SAE */
|
|
++ else if (!strcmp(mode, "mesh") && !strcmp(wpa_key_mgmt, "UNKNOWN"))
|
|
++ {
|
|
++ c->enabled = 1;
|
|
++ c->wpa_version = 4;
|
|
++ c->auth_suites = IWINFO_KMGMT_SAE;
|
|
++ c->pair_ciphers = IWINFO_CIPHER_CCMP;
|
|
++ c->group_ciphers = IWINFO_CIPHER_CCMP;
|
|
++ }
|
|
++
|
|
++ /* WPA */
|
|
++ else
|
|
++ {
|
|
++ parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
|
|
++ parse_wpa_ciphers(wpa_groupwise, &c->group_ciphers);
|
|
++
|
|
++ p = wpa_key_mgmt;
|
|
++
|
|
++ if (!strncmp(p, "WPA2-", 5) || !strncmp(p, "WPA2/", 5))
|
|
++ {
|
|
++ p += 5;
|
|
++ wpa_version = 2;
|
|
++ }
|
|
++ else if (!strncmp(p, "WPA-", 4))
|
|
++ {
|
|
++ p += 4;
|
|
++ wpa_version = 1;
|
|
++ }
|
|
++
|
|
++ parse_wpa_suites(p, wpa_version, &c->wpa_version, &c->auth_suites, atoi(pmf));
|
|
++
|
|
++ c->enabled = !!(c->wpa_version && c->auth_suites);
|
|
++ }
|
|
++
|
|
++ return 0;
|
|
++ }
|
|
++
|
|
++ /* Hostapd */
|
|
++ else if (nl80211_hostapd_query(ifname,
|
|
++ "wpa", wpa, sizeof(wpa),
|
|
++ "wpa_key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
|
|
++ "wpa_pairwise", wpa_pairwise, sizeof(wpa_pairwise),
|
|
++ "ieee80211w", ieee80211w, sizeof(ieee80211w),
|
|
++ "auth_algs", auth_algs, sizeof(auth_algs),
|
|
++ "wep_key0", wep_key0, sizeof(wep_key0),
|
|
++ "wep_key1", wep_key1, sizeof(wep_key1),
|
|
++ "wep_key2", wep_key2, sizeof(wep_key2),
|
|
++ "wep_key3", wep_key3, sizeof(wep_key3)))
|
|
++ {
|
|
++ c->wpa_version = 0;
|
|
++
|
|
++ if (wpa_key_mgmt[0])
|
|
++ {
|
|
++ for (p = strtok(wpa_key_mgmt, " \t"); p != NULL; p = strtok(NULL, " \t"))
|
|
++ {
|
|
++ if (!strncmp(p, "WPA-", 4))
|
|
++ p += 4;
|
|
++
|
|
++ if (!strncmp(p, "FT-", 3))
|
|
++ p += 3;
|
|
++
|
|
++ parse_wpa_suites(p, atoi(wpa), &c->wpa_version, &c->auth_suites, atoi(ieee80211w));
|
|
++ }
|
|
++
|
|
++ c->enabled = c->wpa_version ? 1 : 0;
|
|
++ }
|
|
++
|
|
++ if (wpa_pairwise[0])
|
|
++ parse_wpa_ciphers(wpa_pairwise, &c->pair_ciphers);
|
|
++
|
|
++ if (auth_algs[0])
|
|
++ {
|
|
++ switch (atoi(auth_algs))
|
|
++ {
|
|
++ case 1:
|
|
++ c->auth_algs |= IWINFO_AUTH_OPEN;
|
|
++ break;
|
|
++
|
|
++ case 2:
|
|
++ c->auth_algs |= IWINFO_AUTH_SHARED;
|
|
++ break;
|
|
++
|
|
++ case 3:
|
|
++ c->auth_algs |= IWINFO_AUTH_OPEN;
|
|
++ c->auth_algs |= IWINFO_AUTH_SHARED;
|
|
++ break;
|
|
++ }
|
|
++
|
|
++ c->pair_ciphers |= nl80211_check_wepkey(wep_key0);
|
|
++ c->pair_ciphers |= nl80211_check_wepkey(wep_key1);
|
|
++ c->pair_ciphers |= nl80211_check_wepkey(wep_key2);
|
|
++ c->pair_ciphers |= nl80211_check_wepkey(wep_key3);
|
|
++
|
|
++ c->enabled = (c->auth_algs && c->pair_ciphers) ? 1 : 0;
|
|
++ }
|
|
++
|
|
++ c->group_ciphers = c->pair_ciphers;
|
|
++
|
|
++ return 0;
|
|
++ }
|
|
++
|
|
++ /* Ad-Hoc or Mesh interfaces without wpa_supplicant are open */
|
|
++ else if (!nl80211_get_mode(ifname, &opmode) &&
|
|
++ (opmode == IWINFO_OPMODE_ADHOC ||
|
|
++ opmode == IWINFO_OPMODE_MESHPOINT))
|
|
++ {
|
|
++ c->enabled = 0;
|
|
++
|
|
++ return 0;
|
|
++ }
|
|
++
|
|
++
|
|
++ return -1;
|
|
++}
|
|
++
|
|
++const struct iwinfo_ops dot11ah_ops = {
|
|
++ .name = "dot11ah",
|
|
++ .probe = dot11ah_probe,
|
|
++ .channel = dot11ah_get_channel,
|
|
++ .center_chan1 = dot11ah_get_center_chan1,
|
|
++ .center_chan2 = dot11ah_get_center_chan2,
|
|
++ .frequency = dot11ah_get_frequency,
|
|
++ .frequency_offset = nl80211_get_frequency_offset,
|
|
++ .txpower = nl80211_get_txpower,
|
|
++ .txpower_offset = nl80211_get_txpower_offset,
|
|
++ .bitrate = dot11ah_get_bitrate,
|
|
++ .signal = nl80211_get_signal,
|
|
++ .noise = dot11ah_get_noise,
|
|
++ .quality = nl80211_get_quality,
|
|
++ .quality_max = nl80211_get_quality_max,
|
|
++ .mbssid_support = nl80211_get_mbssid_support,
|
|
++ .hwmodelist = dot11ah_get_hwmodelist,
|
|
++ .htmodelist = dot11ah_get_htmodelist,
|
|
++ .htmode = dot11ah_get_htmode,
|
|
++ .mode = nl80211_get_mode,
|
|
++ .ssid = nl80211_get_ssid,
|
|
++ .bssid = nl80211_get_bssid,
|
|
++ .country = dot11ah_get_country,
|
|
++ .hardware_id = nl80211_get_hardware_id,
|
|
++ .hardware_name = nl80211_get_hardware_name,
|
|
++ .encryption = dot11ah_get_encryption,
|
|
++ .phyname = nl80211_get_phyname,
|
|
++ .assoclist = dot11ah_get_assoclist,
|
|
++ .txpwrlist = nl80211_get_txpwrlist,
|
|
++ .scanlist = dot11ah_get_scanlist,
|
|
++ .freqlist = dot11ah_get_freqlist,
|
|
++ .countrylist = dot11ah_get_countrylist,
|
|
++ .survey = nl80211_get_survey,
|
|
++ .lookup_phy = nl80211_lookup_phyname,
|
|
++ .phy_path = nl80211_phy_path,
|
|
++ .close = nl80211_close
|
|
++};
|
|
+\ No newline at end of file
|
|
+diff --git a/iwinfo_utils.c b/iwinfo_utils.c
|
|
+index d96cbb3..a7c8abe 100644
|
|
+--- a/iwinfo_utils.c
|
|
++++ b/iwinfo_utils.c
|
|
+@@ -2,6 +2,7 @@
|
|
+ * iwinfo - Wireless Information Library - Shared utility routines
|
|
+ *
|
|
+ * Copyright (C) 2010 Jo-Philipp Wich <xm@subsignal.org>
|
|
++ * Copyright 2022 Morse Micro
|
|
+ *
|
|
+ * The iwinfo library is free software: you can redistribute it and/or
|
|
+ * modify it under the terms of the GNU General Public License version 2
|
|
+@@ -151,8 +152,8 @@ uint8_t iwinfo_ghz2band(uint32_t ghz)
|
|
+
|
|
+ size_t iwinfo_format_hwmodes(int modes, char *buf, size_t len)
|
|
+ {
|
|
+- // bit numbers as per IWINFO_80211_*: ad ac ax a b g n
|
|
+- const int order[IWINFO_80211_COUNT] = { 5, 4, 6, 0, 1, 2, 3 };
|
|
++ // bit numbers as per IWINFO_80211_*: ad ac ax ah a b g n
|
|
++ const int order[IWINFO_80211_COUNT] = { 5, 4, 6, 7, 0, 1, 2, 3 };
|
|
+ size_t res = 0;
|
|
+ int i;
|
|
+
|
|
+@@ -599,6 +600,25 @@ void iwinfo_parse_rsn(struct iwinfo_crypto_entry *c, uint8_t *data, uint8_t len,
|
|
+ len -= 2 + (count * 4);
|
|
+ }
|
|
+
|
|
++void iwinfo_parse_rsnxe(struct iwinfo_crypto_entry *c, uint8_t *data, uint8_t len)
|
|
++{
|
|
++ if (len > 2)
|
|
++ return;
|
|
++
|
|
++ c->prot_twt = (data[0] >> 4) & 0x1;
|
|
++ c->sae_h2e = (data[0] >> 5) & 0x1;
|
|
++ c->sae_pk = (data[0] >> 6) & 0x1;
|
|
++ len -= 1;
|
|
++ data += 1;
|
|
++
|
|
++ if (len == 0)
|
|
++ return;
|
|
++
|
|
++ c->secure_ltf = (data[0]) & 0x1;
|
|
++ c->secure_rtt = (data[0] >> 1) & 0x1;
|
|
++ c->prot_range_neg = (data[0] >> 2) & 0x1;
|
|
++}
|
|
++
|
|
+ struct uci_section *iwinfo_uci_get_radio(const char *name, const char *type)
|
|
+ {
|
|
+ struct uci_ptr ptr = {
|
|
--
|
|
2.34.1
|
|
|