[][MAC80211][misc][preliminary version of Filogic 680 on Filogic 880]

[Description]
Add preliminary version of Filogic 680 on Filogic 880.
Based on mt76 revision: 269df4b01f27 ("wifi: mt76: fix rx checksum offload on mt7615/mt7915/mt7921")

This series adds mt7996, a new mac80211 driver for MediaTek Wi-Fi 7
(802.11be) devices, which currently supports AP, station, mesh, and
monitor modes.

mt7996 first supports Filogic 680, which is a Wi-Fi 7 chipset supporting
concurrent tri-band operation at 6 GHz, 5 GHz, and 2.4 GHz with 4x4
antennas on each band. There are several variants that will be added in
upcoming patches. For more details, please refer to [1].

mt7996 supports only Wi-Fi 6E at the moment, whereas Wi-Fi 7 and its
specific features are work in progress. They will be introduced in
further patches.

[1] https://corp.mediatek.com/news-events/press-releases/mediatek-announces-worlds-first-complete-wi-fi-7-platforms-for-access-points-and-clients

[Release-log]
N/A

Change-Id: I7d3dea2626556751c9b0462e587743fad5287be0
Reviewed-on: https://gerrit.mediatek.inc/c/openwrt/feeds/mtk_openwrt_feeds/+/6709775
diff --git a/autobuild_mac80211_release/mt7988_mt7996_mac80211/package/kernel/mt76/src/scripts/single-sku.pl b/autobuild_mac80211_release/mt7988_mt7996_mac80211/package/kernel/mt76/src/scripts/single-sku.pl
new file mode 100644
index 0000000..9e381d2
--- /dev/null
+++ b/autobuild_mac80211_release/mt7988_mt7996_mac80211/package/kernel/mt76/src/scripts/single-sku.pl
@@ -0,0 +1,333 @@
+#!/usr/bin/perl
+use strict;
+use 5.010;
+
+my $indent = 0;
+
+my %chip_types = (
+	mt7615 => "mt7615",
+	mt7622 => "mt7615",
+	mt7663 => "mt7615"
+);
+
+my $chip = shift @ARGV;
+my $chip_type;
+
+$chip or usage();
+
+sub usage() {
+	print STDERR <<EOF;
+Usage: $0 <chip> [options] <file> [[options] <file>...]
+
+Options:
+	country=XX:		Set country code
+	regdomain=XX:		Set regdomain
+
+Multiple country codes/regdomains per file are supported
+
+EOF
+	exit(1);
+}
+
+sub convert_array($) {
+	my $array = shift;
+
+	return unless $array;
+	foreach my $i (0 .. $#{$array}) {
+		$array->[$i] = int($array->[$i] * 2);
+	}
+
+	return $array;
+}
+
+sub parse_channel($$$) {
+	my $band = shift;
+	my $channels = $band->{channels};
+
+	my $ch = shift;
+	my $line = shift;
+
+	my @data = split /\s+/, $line;
+	my $data = join(" ", @data);
+
+	my $channel = {
+		chlist => [ ],
+		data => $data,
+		mcs => [],
+	};
+
+	$channels->{$ch} = $channel;
+
+	$band->{type} eq '2' and do {
+		$channel->{"rates-cck"} = convert_array([
+			$data[0], $data[0],
+			$data[1], $data[1]
+		]);
+		splice @data, 0, 2;
+	};
+
+	$channel->{"rates-ofdm"} = convert_array([
+		$data[0], $data[0],
+		$data[1], $data[1],
+		$data[2], $data[2],
+		$data[3], $data[4]
+	]);
+	splice @data, 0, 5;
+
+	my @bw = ( "bw20", "bw40", "bw80", "bw160" );
+	$band->{type} eq '2' and @bw = ( "bw20", "bw40" );
+
+	foreach my $bw (@bw) {
+		push @{$channel->{"rates-mcs"}}, convert_array([
+			$data[0],
+			$data[1], $data[1],
+			$data[2], $data[2],
+			$data[3], $data[3],
+			$data[4],
+			$data[5],
+			$data[6],
+		]);
+		splice @data, 0, 7;
+	};
+
+	@data > 0 and do {
+		$channel->{"txs-delta"} = convert_array([ reverse splice @data, 0, 3 ]);
+		delete $channel->{"txs-delta"} if join("", @{$channel->{"txs-delta"}}) =~ /^0+$/;
+	};
+}
+
+sub read_data($) {
+	my $file = shift;
+	my $band;
+	my %bands;
+
+	open FILE, "<", $file or die "Can't open file $file\n";
+	while (<FILE>) {
+		chomp;
+
+		/^Band: (2.4|5)G / and do {
+			$band = $1;
+			$band eq '2.4' and $band = "2";
+			$bands{$band} = {
+				type => $band,
+				channels => {},
+			};
+		};
+		/^Ch(\d+)\s+(.+?)\s*$/ and parse_channel($bands{$band}, $1, $2);
+	}
+	close FILE;
+
+	return \%bands;
+}
+
+sub find_matching_channel($$) {
+	my $band = shift;
+	my $channels = $band->{channels};
+
+	my $ch_idx = shift;
+	my $ch = $channels->{$ch_idx};
+
+	foreach my $cur (sort { $a <=> $b } keys %$channels) {
+		my $cur_ch;
+
+		return undef if $cur >= $ch_idx;
+
+		$cur_ch = $channels->{$cur};
+		$cur_ch->{data} eq $ch->{data} and return $cur_ch;
+	}
+}
+
+sub optimize_channels($) {
+	my $band = shift;
+	my $channels = $band->{channels};
+	my $prev;
+	my $prev_chlist;
+
+	foreach my $ch_idx (sort { $a <=> $b } keys %$channels) {
+		my $ch = $channels->{$ch_idx};
+
+		$prev and ($ch->{data} eq $prev->{data}) and do {
+			$prev_chlist->[1] = $ch_idx;
+			delete $channels->{$ch_idx};
+			next;
+		};
+
+		$prev = find_matching_channel($band, $ch_idx);
+		if ($prev) {
+			delete $channels->{$ch_idx};
+		} else {
+			$prev = $ch;
+		}
+
+		$prev_chlist = [ $ch_idx, $ch_idx ];
+		push @{$prev->{chlist}}, $prev_chlist;
+	}
+}
+
+sub add_line {
+	my $line = shift;
+	print "".("\t" x $indent).$line;
+}
+
+sub array_str($) {
+	my $a = shift;
+
+	return "<".join(" ", @$a).">";
+}
+
+sub string_array_str($) {
+	my $a = shift;
+
+	return join(", ", map { "\"$_\"" } @$a);
+}
+
+sub add_named_array($$) {
+	my $ch = shift;
+	my $type = shift;
+
+	return unless $ch->{$type};
+	add_line("$type = ".array_str($ch->{$type}).";\n");
+}
+
+sub add_named_string_array($$) {
+	my $ch = shift;
+	my $type = shift;
+
+	return unless $ch->{$type} and @{$ch->{$type}} > 0;
+	add_line("$type = ".string_array_str($ch->{$type}).";\n");
+}
+
+sub add_multi_array($$) {
+	my $name = shift;
+	my $a = shift;
+
+	add_line("$name =");
+	if (@$a > 1) {
+		my $first = 1;
+
+		print "\n";
+		$indent++;
+		foreach my $v (@$a) {
+			$first or print ",\n";
+			undef $first;
+
+			add_line(array_str($v));
+		}
+		$indent--;
+	} else {
+		print " ".array_str($a->[0]);
+	}
+	print ";\n";
+}
+
+sub print_txpower($) {
+	my $ch = shift;
+	my @data;
+
+	add_named_array($ch, "txs-delta");
+	add_named_array($ch, "rates-cck");
+	add_named_array($ch, "rates-ofdm");
+
+	my $prev;
+	foreach my $v (@{$ch->{"rates-mcs"}}) {
+		my $val = [1, @{$v}];
+
+		if ($prev and (array_str($v) eq array_str($prev))) {
+			$data[$#data]->[0]++;
+		} else {
+			push @data, $val;
+		}
+
+		$prev = $v;
+	}
+
+	add_multi_array("rates-mcs", \@data);
+};
+
+sub print_channels($) {
+	my $band = shift;
+	my $channels = $band->{channels};
+	my $r = 0;
+
+	foreach my $ch_idx (sort { $a <=> $b } keys %$channels) {
+		my $ch = $channels->{$ch_idx};
+		my $first = 1;
+
+		add_line("r$r {\n");
+		$indent++;
+
+		add_multi_array("channels", $ch->{chlist});
+		print_txpower($ch);
+
+		$indent--;
+		add_line("};\n");
+
+		$r++;
+	}
+}
+
+sub print_bands($) {
+	my $bands = shift;
+
+	foreach my $band_idx (sort keys %$bands) {
+		my $band = $bands->{$band_idx};
+
+		optimize_channels($band);
+
+		add_line("txpower-".$band->{type}."g {\n");
+		$indent++;
+
+		print_channels($band);
+
+		$indent--;
+		add_line("};\n");
+	};
+}
+
+my @files;
+my $cur = {};
+
+$chip_type = $chip_types{$chip} or die "Unsupported chip type '$chip', supported chip types: ".join(", ",sort keys %chip_types)."\n";
+
+while (@ARGV > 0) {
+	my $arg = shift @ARGV;
+
+	$cur or $cur = {
+		country => [],
+		regdomain => [],
+	};
+
+	if ($arg =~ /country=(\w+)$/) {
+		push @{$cur->{country}}, $1;
+	} elsif ($arg =~ /regdomain=(\w+)$/) {
+		push @{$cur->{regdomain}}, $1;
+	} else {
+		$cur->{bands} = read_data($arg);
+		push @files, $cur;
+		$cur = undef;
+	}
+}
+
+
+add_line("power-limits {\n");
+$indent++;
+
+my $count = 0;
+
+foreach my $domain (@files) {
+	my $name = "r$count";
+	$count++;
+
+	add_line("$name {\n");
+	$indent++;
+
+	add_named_string_array($domain, "country");
+	add_named_string_array($domain, "regdomain");
+	print_bands($domain->{bands});
+
+	$indent--;
+	add_line("};\n");
+}
+
+$indent--;
+add_line("};\n");