mirror of
				https://github.com/Ysurac/openmptcprouter.git
				synced 2025-03-09 15:40:20 +00:00 
			
		
		
		
	Update Linux 5.4 kernel and clean some files
This commit is contained in:
		
							parent
							
								
									272324aa1a
								
							
						
					
					
						commit
						0a55523ea5
					
				
					 26 changed files with 870 additions and 4059 deletions
				
			
		
							
								
								
									
										6
									
								
								build.sh
									
										
									
									
									
								
							
							
						
						
									
										6
									
								
								build.sh
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -94,9 +94,9 @@ fi
 | 
			
		|||
if [ "$OMR_OPENWRT" = "default" ]; then
 | 
			
		||||
	if [ "$OMR_KERNEL" = "5.4" ]; then
 | 
			
		||||
		# Use OpenWrt 21.02 for 5.4 kernel
 | 
			
		||||
		_get_repo "$OMR_TARGET/source" https://github.com/openwrt/openwrt "f441be3921c769b732f0148f005d4f1bbace0508"
 | 
			
		||||
		_get_repo feeds/packages https://github.com/openwrt/packages "ab94e0709a9c796d34d723ddba44380f7b3d8698"
 | 
			
		||||
		_get_repo feeds/luci https://github.com/openwrt/luci "0818d835cacd9fa75b8685aabe6378ac09b95145"
 | 
			
		||||
		_get_repo "$OMR_TARGET/source" https://github.com/openwrt/openwrt "864bba55d8714a64abdf94cfb835450b8cd7789e"
 | 
			
		||||
		_get_repo feeds/packages https://github.com/openwrt/packages "793e7ee484ae4ec37b1cd920b4032dde3cae69cc"
 | 
			
		||||
		_get_repo feeds/luci https://github.com/openwrt/luci "701ea947fc920e63d14d8efb8287097fd63442ca"
 | 
			
		||||
	else
 | 
			
		||||
		_get_repo "$OMR_TARGET/source" https://github.com/openwrt/openwrt "02de391b086dd2b7a72c2394cfb66cec666a51c1"
 | 
			
		||||
		_get_repo feeds/packages https://github.com/openwrt/packages "7b2dd3e9efbc20ef4e7f47f60c3db9aaef37c0a5"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,12 +6,13 @@ ifdef CONFIG_TESTING_KERNEL
 | 
			
		|||
  KERNEL_PATCHVER:=$(KERNEL_TESTING_PATCHVER)
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
LINUX_VERSION-5.4 = .132
 | 
			
		||||
LINUX_VERSION-5.4 = .182
 | 
			
		||||
LINUX_VERSION-5.10 = .64
 | 
			
		||||
LINUX_VERSION-5.14 = .6
 | 
			
		||||
LINUX_VERSION-5.15 = .17
 | 
			
		||||
 | 
			
		||||
LINUX_KERNEL_HASH-5.4.132 = 8466adbfb3579e751ede683496df7bb20f258b5f882250f3dd82be63736d00ef
 | 
			
		||||
LINUX_KERNEL_HASH-5.4.182 = b2f1201f64f010e9e3c85d6f303a559a7944a80a0244a86b8f5035bd23f1f40d
 | 
			
		||||
LINUX_KERNEL_HASH-5.10.64 = 3eb84bd24a2de2b4749314e34597c02401c5d6831b055ed5224adb405c35e30a
 | 
			
		||||
LINUX_KERNEL_HASH-5.14.6 = 54848c1268771ee3515e4c33e29abc3f1fa90d8144894cce6d0ebc3b158bccec
 | 
			
		||||
LINUX_KERNEL_HASH-5.15.4 = 549d0fb75e65f6158e6f4becc648f249d386843da0e1211460bde8b1ea99cbca
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										8
									
								
								root/package/network/wwan/files/data/05c6-9215
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								root/package/network/wwan/files/data/05c6-9215
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
{
 | 
			
		||||
	"desc": "Quectel EC20",
 | 
			
		||||
	"type": "gobinet",
 | 
			
		||||
	"control": 2,
 | 
			
		||||
	"boudrate": 115200,
 | 
			
		||||
	"stop_bits": 8,
 | 
			
		||||
	"gps": 1
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0121
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0121
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
{
 | 
			
		||||
	"desc": "Quectel EC21",
 | 
			
		||||
	"type": "gobinet",
 | 
			
		||||
	"control": 2,
 | 
			
		||||
	"boudrate": 115200,
 | 
			
		||||
	"stop_bits": 8,
 | 
			
		||||
	"gps": 1,
 | 
			
		||||
	"ep_iface": 4,
 | 
			
		||||
	"dl_max_size": 4096,
 | 
			
		||||
	"dl_max_datagrams": 16
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0296
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0296
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
{
 | 
			
		||||
	"desc": "Quectel BG96",
 | 
			
		||||
	"type": "gobinet",
 | 
			
		||||
	"control": 3,
 | 
			
		||||
	"boudrate": 115200,
 | 
			
		||||
	"stop_bits": 8,
 | 
			
		||||
	"gps": 1,
 | 
			
		||||
        "ep_iface": 4,
 | 
			
		||||
        "dl_max_size": 2048,
 | 
			
		||||
        "dl_max_datagrams": 8
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0306
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0306
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
{
 | 
			
		||||
	"desc": "Quectel EG06",
 | 
			
		||||
	"type": "gobinet",
 | 
			
		||||
	"control": 2,
 | 
			
		||||
	"boudrate": 115200,
 | 
			
		||||
	"stop_bits": 8,
 | 
			
		||||
	"gps": 1,
 | 
			
		||||
	"ep_iface": 4,
 | 
			
		||||
	"dl_max_size": 16384,
 | 
			
		||||
	"dl_max_datagrams": 32
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0512
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								root/package/network/wwan/files/data/2c7c-0512
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
{
 | 
			
		||||
	"desc": "Quectel EG12/EG18",
 | 
			
		||||
	"type": "gobinet",
 | 
			
		||||
	"control": 2,
 | 
			
		||||
	"boudrate": 115200,
 | 
			
		||||
	"stop_bits": 8,
 | 
			
		||||
	"gps": 1,
 | 
			
		||||
	"ep_iface": 4,
 | 
			
		||||
	"dl_max_size": 16384,
 | 
			
		||||
	"dl_max_datagrams": 32
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,53 +0,0 @@
 | 
			
		|||
From: Bui Quang Minh @ 2021-01-26  8:26 UTC (permalink / raw)
 | 
			
		||||
  To: ast, daniel, davem, kuba, hawk, john.fastabend, andrii, kafai,
 | 
			
		||||
	songliubraving, yhs, kpsingh, jakub, lmb
 | 
			
		||||
  Cc: netdev, bpf, linux-kernel, minhquangbui99
 | 
			
		||||
 | 
			
		||||
In 32-bit architecture, the result of sizeof() is a 32-bit integer so
 | 
			
		||||
the expression becomes the multiplication between 2 32-bit integer which
 | 
			
		||||
can potentially leads to integer overflow. As a result,
 | 
			
		||||
bpf_map_area_alloc() allocates less memory than needed.
 | 
			
		||||
 | 
			
		||||
Fix this by casting 1 operand to u64.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Bui Quang Minh <minhquangbui99@gmail.com>
 | 
			
		||||
---
 | 
			
		||||
 kernel/bpf/devmap.c | 4 ++--
 | 
			
		||||
 net/core/sock_map.c | 2 +-
 | 
			
		||||
 2 files changed, 3 insertions(+), 3 deletions(-)
 | 
			
		||||
 | 
			
		||||
Index: linux-5.4.147/kernel/bpf/devmap.c
 | 
			
		||||
===================================================================
 | 
			
		||||
--- linux-5.4.147.orig/kernel/bpf/devmap.c
 | 
			
		||||
+++ linux-5.4.147/kernel/bpf/devmap.c
 | 
			
		||||
@@ -94,7 +94,7 @@ static struct hlist_head *dev_map_create
 | 
			
		||||
 	int i;
 | 
			
		||||
 	struct hlist_head *hash;
 | 
			
		||||
 
 | 
			
		||||
-	hash = bpf_map_area_alloc(entries * sizeof(*hash), numa_node);
 | 
			
		||||
+	hash = bpf_map_area_alloc((u64) entries * sizeof(*hash), numa_node);
 | 
			
		||||
 	if (hash != NULL)
 | 
			
		||||
 		for (i = 0; i < entries; i++)
 | 
			
		||||
 			INIT_HLIST_HEAD(&hash[i]);
 | 
			
		||||
@@ -159,7 +159,7 @@ static int dev_map_init_map(struct bpf_d
 | 
			
		||||
 
 | 
			
		||||
 		spin_lock_init(&dtab->index_lock);
 | 
			
		||||
 	} else {
 | 
			
		||||
-		dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries *
 | 
			
		||||
+		dtab->netdev_map = bpf_map_area_alloc((u64) dtab->map.max_entries *
 | 
			
		||||
 						      sizeof(struct bpf_dtab_netdev *),
 | 
			
		||||
 						      dtab->map.numa_node);
 | 
			
		||||
 		if (!dtab->netdev_map)
 | 
			
		||||
Index: linux-5.4.147/net/core/sock_map.c
 | 
			
		||||
===================================================================
 | 
			
		||||
--- linux-5.4.147.orig/net/core/sock_map.c
 | 
			
		||||
+++ linux-5.4.147/net/core/sock_map.c
 | 
			
		||||
@@ -48,7 +48,7 @@ static struct bpf_map *sock_map_alloc(un
 | 
			
		||||
 	if (err)
 | 
			
		||||
 		goto free_stab;
 | 
			
		||||
 
 | 
			
		||||
-	stab->sks = bpf_map_area_alloc(stab->map.max_entries *
 | 
			
		||||
+	stab->sks = bpf_map_area_alloc((u64) stab->map.max_entries *
 | 
			
		||||
 				       sizeof(struct sock *),
 | 
			
		||||
 				       stab->map.numa_node);
 | 
			
		||||
 	if (stab->sks)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,115 +0,0 @@
 | 
			
		|||
From: Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
Date: Wed, 13 Jun 2018 12:33:39 +0200
 | 
			
		||||
Subject: [PATCH] netfilter: nf_flow_table: fix offloaded connection timeout
 | 
			
		||||
 corner case
 | 
			
		||||
 | 
			
		||||
The full teardown of offloaded flows is deferred to a gc work item,
 | 
			
		||||
however processing of packets by netfilter needs to happen immediately
 | 
			
		||||
after a teardown is requested, because the conntrack state needs to be
 | 
			
		||||
fixed up.
 | 
			
		||||
 | 
			
		||||
Since the IPS_OFFLOAD_BIT is still kept until the teardown is complete,
 | 
			
		||||
the netfilter conntrack gc can accidentally bump the timeout of a
 | 
			
		||||
connection where offload was just stopped, causing a conntrack entry
 | 
			
		||||
leak.
 | 
			
		||||
 | 
			
		||||
Fix this by moving the conntrack timeout bumping from conntrack core to
 | 
			
		||||
the nf_flow_offload and add a check to prevent bogus timeout bumps.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
--- a/net/netfilter/nf_conntrack_core.c
 | 
			
		||||
+++ b/net/netfilter/nf_conntrack_core.c
 | 
			
		||||
@@ -1207,18 +1207,6 @@ static bool gc_worker_can_early_drop(con
 | 
			
		||||
 	return false;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
-#define	DAY	(86400 * HZ)
 | 
			
		||||
-
 | 
			
		||||
-/* Set an arbitrary timeout large enough not to ever expire, this save
 | 
			
		||||
- * us a check for the IPS_OFFLOAD_BIT from the packet path via
 | 
			
		||||
- * nf_ct_is_expired().
 | 
			
		||||
- */
 | 
			
		||||
-static void nf_ct_offload_timeout(struct nf_conn *ct)
 | 
			
		||||
-{
 | 
			
		||||
-	if (nf_ct_expires(ct) < DAY / 2)
 | 
			
		||||
-		ct->timeout = nfct_time_stamp + DAY;
 | 
			
		||||
-}
 | 
			
		||||
-
 | 
			
		||||
 static void gc_worker(struct work_struct *work)
 | 
			
		||||
 {
 | 
			
		||||
 	unsigned long end_time = jiffies + GC_SCAN_MAX_DURATION;
 | 
			
		||||
@@ -1250,11 +1238,9 @@ static void gc_worker(struct work_struct
 | 
			
		||||
 
 | 
			
		||||
 			tmp = nf_ct_tuplehash_to_ctrack(h);
 | 
			
		||||
 
 | 
			
		||||
			scanned++;
 | 
			
		||||
-			if (test_bit(IPS_OFFLOAD_BIT, &tmp->status)) {
 | 
			
		||||
-				nf_ct_offload_timeout(tmp);
 | 
			
		||||
+			if (test_bit(IPS_OFFLOAD_BIT, &tmp->status))
 | 
			
		||||
 				continue;
 | 
			
		||||
-			}
 | 
			
		||||
 
 | 
			
		||||
 			if (nf_ct_is_expired(tmp)) {
 | 
			
		||||
 				nf_ct_gc_expired(tmp);
 | 
			
		||||
--- a/net/netfilter/nf_flow_table_core.c
 | 
			
		||||
+++ b/net/netfilter/nf_flow_table_core.c
 | 
			
		||||
@@ -198,10 +198,29 @@ static const struct rhashtable_params nf
 | 
			
		||||
 	.automatic_shrinking	= true,
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+#define        DAY     (86400 * HZ)
 | 
			
		||||
+
 | 
			
		||||
+/* Set an arbitrary timeout large enough not to ever expire, this save
 | 
			
		||||
+ * us a check for the IPS_OFFLOAD_BIT from the packet path via
 | 
			
		||||
+ * nf_ct_is_expired().
 | 
			
		||||
+ */
 | 
			
		||||
+static void nf_ct_offload_timeout(struct flow_offload *flow)
 | 
			
		||||
+{
 | 
			
		||||
+	struct flow_offload_entry *entry;
 | 
			
		||||
+	struct nf_conn *ct;
 | 
			
		||||
+
 | 
			
		||||
+	entry = container_of(flow, struct flow_offload_entry, flow);
 | 
			
		||||
+	ct = entry->ct;
 | 
			
		||||
+
 | 
			
		||||
+	if (nf_ct_expires(ct) < DAY / 2)
 | 
			
		||||
+		ct->timeout = nfct_time_stamp + DAY;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
 | 
			
		||||
 {
 | 
			
		||||
 	int err;
 | 
			
		||||
 
 | 
			
		||||
+	nf_ct_offload_timeout(flow);
 | 
			
		||||
 	flow->timeout = (u32)jiffies + NF_FLOW_TIMEOUT;
 | 
			
		||||
 
 | 
			
		||||
 	err = rhashtable_insert_fast(&flow_table->rhashtable,
 | 
			
		||||
@@ -304,6 +323,7 @@ nf_flow_table_iterate(struct nf_flowtabl
 | 
			
		||||
 	rhashtable_walk_start(&hti);
 | 
			
		||||
 
 | 
			
		||||
 	while ((tuplehash = rhashtable_walk_next(&hti))) {
 | 
			
		||||
+
 | 
			
		||||
 		if (IS_ERR(tuplehash)) {
 | 
			
		||||
 			if (PTR_ERR(tuplehash) != -EAGAIN) {
 | 
			
		||||
 				err = PTR_ERR(tuplehash);
 | 
			
		||||
@@ -328,10 +348,17 @@ static void nf_flow_offload_gc_step(stru
 | 
			
		||||
 {
 | 
			
		||||
 	struct nf_flowtable *flow_table = data;
 | 
			
		||||
 	struct flow_offload_entry *e;
 | 
			
		||||
+	bool teardown;
 | 
			
		||||
 
 | 
			
		||||
 	e = container_of(flow, struct flow_offload_entry, flow);
 | 
			
		||||
-	if (nf_flow_has_expired(flow) || nf_ct_is_dying(e->ct) ||
 | 
			
		||||
-	    (flow->flags & (FLOW_OFFLOAD_DYING | FLOW_OFFLOAD_TEARDOWN)))
 | 
			
		||||
+
 | 
			
		||||
+	teardown = flow->flags & (FLOW_OFFLOAD_DYING |
 | 
			
		||||
+				  FLOW_OFFLOAD_TEARDOWN);
 | 
			
		||||
+
 | 
			
		||||
+	if (!teardown)
 | 
			
		||||
+		nf_ct_offload_timeout(flow);
 | 
			
		||||
+
 | 
			
		||||
+	if (nf_flow_has_expired(flow) || teardown)
 | 
			
		||||
 		flow_offload_del(flow_table, flow);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,554 @@
 | 
			
		|||
From: Pablo Neira Ayuso <pablo@netfilter.org>
 | 
			
		||||
Date: Thu, 11 Jan 2018 16:32:00 +0100
 | 
			
		||||
Subject: [PATCH] netfilter: nf_flow_table: add hardware offload support
 | 
			
		||||
 | 
			
		||||
This patch adds the infrastructure to offload flows to hardware, in case
 | 
			
		||||
the nic/switch comes with built-in flow tables capabilities.
 | 
			
		||||
 | 
			
		||||
If the hardware comes with no hardware flow tables or they have
 | 
			
		||||
limitations in terms of features, the existing infrastructure falls back
 | 
			
		||||
to the software flow table implementation.
 | 
			
		||||
 | 
			
		||||
The software flow table garbage collector skips entries that resides in
 | 
			
		||||
the hardware, so the hardware will be responsible for releasing this
 | 
			
		||||
flow table entry too via flow_offload_dead().
 | 
			
		||||
 | 
			
		||||
Hardware configuration, either to add or to delete entries, is done from
 | 
			
		||||
the hardware offload workqueue, to ensure this is done from user context
 | 
			
		||||
given that we may sleep when grabbing the mdio mutex.
 | 
			
		||||
 | 
			
		||||
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
 | 
			
		||||
---
 | 
			
		||||
 create mode 100644 net/netfilter/nf_flow_table_hw.c
 | 
			
		||||
 | 
			
		||||
--- a/include/linux/netdevice.h
 | 
			
		||||
+++ b/include/linux/netdevice.h
 | 
			
		||||
@@ -922,6 +922,13 @@ struct devlink;
 | 
			
		||||
 struct tlsdev_ops;
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
+struct flow_offload;
 | 
			
		||||
+
 | 
			
		||||
+enum flow_offload_type {
 | 
			
		||||
+	FLOW_OFFLOAD_ADD	= 0,
 | 
			
		||||
+	FLOW_OFFLOAD_DEL,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
 /*
 | 
			
		||||
  * This structure defines the management hooks for network devices.
 | 
			
		||||
  * The following hooks can be defined; unless noted otherwise, they are
 | 
			
		||||
@@ -1154,6 +1161,10 @@ struct tlsdev_ops;
 | 
			
		||||
  * int (*ndo_bridge_dellink)(struct net_device *dev, struct nlmsghdr *nlh,
 | 
			
		||||
  *			     u16 flags);
 | 
			
		||||
  *
 | 
			
		||||
+ * int (*ndo_flow_offload)(enum flow_offload_type type,
 | 
			
		||||
+ *			   struct flow_offload *flow);
 | 
			
		||||
+ *	Adds/deletes flow entry to/from net device flowtable.
 | 
			
		||||
+ *
 | 
			
		||||
  * int (*ndo_change_carrier)(struct net_device *dev, bool new_carrier);
 | 
			
		||||
  *	Called to change device carrier. Soft-devices (like dummy, team, etc)
 | 
			
		||||
  *	which do not represent real hardware may define this to allow their
 | 
			
		||||
@@ -1401,6 +1412,8 @@ struct net_device_ops {
 | 
			
		||||
 	int			(*ndo_bridge_dellink)(struct net_device *dev,
 | 
			
		||||
 						      struct nlmsghdr *nlh,
 | 
			
		||||
 						      u16 flags);
 | 
			
		||||
+	int			(*ndo_flow_offload)(enum flow_offload_type type,
 | 
			
		||||
+						    struct flow_offload *flow);
 | 
			
		||||
 	int			(*ndo_change_carrier)(struct net_device *dev,
 | 
			
		||||
 						      bool new_carrier);
 | 
			
		||||
 	int			(*ndo_get_phys_port_id)(struct net_device *dev,
 | 
			
		||||
--- a/include/net/netfilter/nf_flow_table.h
 | 
			
		||||
+++ b/include/net/netfilter/nf_flow_table.h
 | 
			
		||||
@@ -21,11 +21,17 @@ struct nf_flowtable_type {
 | 
			
		||||
 	struct module			*owner;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
+enum nf_flowtable_flags {
 | 
			
		||||
+	NF_FLOWTABLE_F_HW		= 0x1,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
 struct nf_flowtable {
 | 
			
		||||
 	struct list_head		list;
 | 
			
		||||
 	struct rhashtable		rhashtable;
 | 
			
		||||
 	const struct nf_flowtable_type	*type;
 | 
			
		||||
+	u32				flags;
 | 
			
		||||
 	struct delayed_work		gc_work;
 | 
			
		||||
+	possible_net_t			ft_net;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 enum flow_offload_tuple_dir {
 | 
			
		||||
@@ -68,6 +74,7 @@ struct flow_offload_tuple_rhash {
 | 
			
		||||
 #define FLOW_OFFLOAD_DNAT	0x2
 | 
			
		||||
 #define FLOW_OFFLOAD_DYING	0x4
 | 
			
		||||
 #define FLOW_OFFLOAD_TEARDOWN	0x8
 | 
			
		||||
+#define FLOW_OFFLOAD_HW		0x10
 | 
			
		||||
 
 | 
			
		||||
 struct flow_offload {
 | 
			
		||||
 	struct flow_offload_tuple_rhash		tuplehash[FLOW_OFFLOAD_DIR_MAX];
 | 
			
		||||
@@ -120,6 +127,22 @@ unsigned int nf_flow_offload_ip_hook(voi
 | 
			
		||||
 unsigned int nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
 | 
			
		||||
 				       const struct nf_hook_state *state);
 | 
			
		||||
 
 | 
			
		||||
+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,
 | 
			
		||||
+			    struct nf_conn *ct);
 | 
			
		||||
+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow);
 | 
			
		||||
+
 | 
			
		||||
+struct nf_flow_table_hw {
 | 
			
		||||
+	struct module	*owner;
 | 
			
		||||
+	void		(*add)(struct net *net, struct flow_offload *flow,
 | 
			
		||||
+			       struct nf_conn *ct);
 | 
			
		||||
+	void		(*del)(struct net *net, struct flow_offload *flow);
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload);
 | 
			
		||||
+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload);
 | 
			
		||||
+
 | 
			
		||||
+extern struct work_struct nf_flow_offload_hw_work;
 | 
			
		||||
+
 | 
			
		||||
 #define MODULE_ALIAS_NF_FLOWTABLE(family)	\
 | 
			
		||||
 	MODULE_ALIAS("nf-flowtable-" __stringify(family))
 | 
			
		||||
 
 | 
			
		||||
--- a/include/uapi/linux/netfilter/nf_tables.h
 | 
			
		||||
+++ b/include/uapi/linux/netfilter/nf_tables.h
 | 
			
		||||
@@ -1516,6 +1516,7 @@ enum nft_object_attributes {
 | 
			
		||||
  * @NFTA_FLOWTABLE_HOOK: netfilter hook configuration(NLA_U32)
 | 
			
		||||
  * @NFTA_FLOWTABLE_USE: number of references to this flow table (NLA_U32)
 | 
			
		||||
  * @NFTA_FLOWTABLE_HANDLE: object handle (NLA_U64)
 | 
			
		||||
+ * @NFTA_FLOWTABLE_FLAGS: flags (NLA_U32)
 | 
			
		||||
  */
 | 
			
		||||
 enum nft_flowtable_attributes {
 | 
			
		||||
 	NFTA_FLOWTABLE_UNSPEC,
 | 
			
		||||
@@ -1525,6 +1526,7 @@ enum nft_flowtable_attributes {
 | 
			
		||||
 	NFTA_FLOWTABLE_USE,
 | 
			
		||||
 	NFTA_FLOWTABLE_HANDLE,
 | 
			
		||||
 	NFTA_FLOWTABLE_PAD,
 | 
			
		||||
+	NFTA_FLOWTABLE_FLAGS,
 | 
			
		||||
 	__NFTA_FLOWTABLE_MAX
 | 
			
		||||
 };
 | 
			
		||||
 #define NFTA_FLOWTABLE_MAX	(__NFTA_FLOWTABLE_MAX - 1)
 | 
			
		||||
--- a/net/netfilter/Kconfig
 | 
			
		||||
+++ b/net/netfilter/Kconfig
 | 
			
		||||
@@ -711,6 +711,15 @@ config NF_FLOW_TABLE
 | 
			
		||||
 
 | 
			
		||||
 	  To compile it as a module, choose M here.
 | 
			
		||||
 
 | 
			
		||||
+config NF_FLOW_TABLE_HW
 | 
			
		||||
+	tristate "Netfilter flow table hardware offload module"
 | 
			
		||||
+	depends on NF_FLOW_TABLE
 | 
			
		||||
+	help
 | 
			
		||||
+	  This option adds hardware offload support for the flow table core
 | 
			
		||||
+	  infrastructure.
 | 
			
		||||
+
 | 
			
		||||
+	  To compile it as a module, choose M here.
 | 
			
		||||
+
 | 
			
		||||
 config NETFILTER_XTABLES
 | 
			
		||||
 	tristate "Netfilter Xtables support (required for ip_tables)"
 | 
			
		||||
 	default m if NETFILTER_ADVANCED=n
 | 
			
		||||
--- a/net/netfilter/Makefile
 | 
			
		||||
+++ b/net/netfilter/Makefile
 | 
			
		||||
@@ -123,6 +123,7 @@ obj-$(CONFIG_NF_FLOW_TABLE)	+= nf_flow_t
 | 
			
		||||
 nf_flow_table-objs := nf_flow_table_core.o nf_flow_table_ip.o
 | 
			
		||||
 
 | 
			
		||||
 obj-$(CONFIG_NF_FLOW_TABLE_INET) += nf_flow_table_inet.o
 | 
			
		||||
+obj-$(CONFIG_NF_FLOW_TABLE_HW)	+= nf_flow_table_hw.o
 | 
			
		||||
 
 | 
			
		||||
 # generic X tables
 | 
			
		||||
 obj-$(CONFIG_NETFILTER_XTABLES) += x_tables.o xt_tcpudp.o
 | 
			
		||||
--- a/net/netfilter/nf_flow_table_core.c
 | 
			
		||||
+++ b/net/netfilter/nf_flow_table_core.c
 | 
			
		||||
@@ -248,10 +248,16 @@ static inline bool nf_flow_has_expired(c
 | 
			
		||||
 	return nf_flow_timeout_delta(flow->timeout) <= 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+static inline bool nf_flow_in_hw(const struct flow_offload *flow)
 | 
			
		||||
+{
 | 
			
		||||
+	return flow->flags & FLOW_OFFLOAD_HW;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static void flow_offload_del(struct nf_flowtable *flow_table,
 | 
			
		||||
 			     struct flow_offload *flow)
 | 
			
		||||
 {
 | 
			
		||||
 	struct flow_offload_entry *e;
 | 
			
		||||
+	struct net *net = read_pnet(&flow_table->ft_net);
 | 
			
		||||
 
 | 
			
		||||
 	rhashtable_remove_fast(&flow_table->rhashtable,
 | 
			
		||||
 			       &flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].node,
 | 
			
		||||
@@ -271,6 +277,9 @@ static void flow_offload_del(struct nf_f
 | 
			
		||||
 	if (!(flow->flags & FLOW_OFFLOAD_TEARDOWN))
 | 
			
		||||
 		flow_offload_fixup_ct_state(e->ct);
 | 
			
		||||
 
 | 
			
		||||
+	if (nf_flow_in_hw(flow))
 | 
			
		||||
+		nf_flow_offload_hw_del(net, flow);
 | 
			
		||||
+
 | 
			
		||||
 	flow_offload_free(flow);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
@@ -490,10 +502,43 @@ int nf_flow_dnat_port(const struct flow_
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(nf_flow_dnat_port);
 | 
			
		||||
 
 | 
			
		||||
+static const struct nf_flow_table_hw __rcu *nf_flow_table_hw_hook __read_mostly;
 | 
			
		||||
+
 | 
			
		||||
+static int nf_flow_offload_hw_init(struct nf_flowtable *flow_table)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct nf_flow_table_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	if (!rcu_access_pointer(nf_flow_table_hw_hook))
 | 
			
		||||
+		request_module("nf-flow-table-hw");
 | 
			
		||||
+
 | 
			
		||||
+	rcu_read_lock();
 | 
			
		||||
+	offload = rcu_dereference(nf_flow_table_hw_hook);
 | 
			
		||||
+	if (!offload)
 | 
			
		||||
+		goto err_no_hw_offload;
 | 
			
		||||
+
 | 
			
		||||
+	if (!try_module_get(offload->owner))
 | 
			
		||||
+		goto err_no_hw_offload;
 | 
			
		||||
+
 | 
			
		||||
+	rcu_read_unlock();
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+
 | 
			
		||||
+err_no_hw_offload:
 | 
			
		||||
+	rcu_read_unlock();
 | 
			
		||||
+
 | 
			
		||||
+	return -EOPNOTSUPP;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 int nf_flow_table_init(struct nf_flowtable *flowtable)
 | 
			
		||||
 {
 | 
			
		||||
 	int err;
 | 
			
		||||
 
 | 
			
		||||
+	if (flowtable->flags & NF_FLOWTABLE_F_HW) {
 | 
			
		||||
+		err = nf_flow_offload_hw_init(flowtable);
 | 
			
		||||
+		if (err)
 | 
			
		||||
+			return err;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	INIT_DEFERRABLE_WORK(&flowtable->gc_work, nf_flow_offload_work_gc);
 | 
			
		||||
 
 | 
			
		||||
 	err = rhashtable_init(&flowtable->rhashtable,
 | 
			
		||||
@@ -534,6 +579,8 @@ static void nf_flow_table_iterate_cleanu
 | 
			
		||||
 {
 | 
			
		||||
 	nf_flow_table_iterate(flowtable, nf_flow_table_do_cleanup, dev);
 | 
			
		||||
 	flush_delayed_work(&flowtable->gc_work);
 | 
			
		||||
+	if (flowtable->flags & NF_FLOWTABLE_F_HW)
 | 
			
		||||
+		flush_work(&nf_flow_offload_hw_work);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
 void nf_flow_table_cleanup(struct net_device *dev)
 | 
			
		||||
@@ -547,6 +594,26 @@ void nf_flow_table_cleanup(struct net_de
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(nf_flow_table_cleanup);
 | 
			
		||||
 
 | 
			
		||||
+struct work_struct nf_flow_offload_hw_work;
 | 
			
		||||
+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_work);
 | 
			
		||||
+
 | 
			
		||||
+/* Give the hardware workqueue the chance to remove entries from hardware.*/
 | 
			
		||||
+static void nf_flow_offload_hw_free(struct nf_flowtable *flowtable)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct nf_flow_table_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	flush_work(&nf_flow_offload_hw_work);
 | 
			
		||||
+
 | 
			
		||||
+	rcu_read_lock();
 | 
			
		||||
+	offload = rcu_dereference(nf_flow_table_hw_hook);
 | 
			
		||||
+	if (!offload) {
 | 
			
		||||
+		rcu_read_unlock();
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+	module_put(offload->owner);
 | 
			
		||||
+	rcu_read_unlock();
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 void nf_flow_table_free(struct nf_flowtable *flow_table)
 | 
			
		||||
 {
 | 
			
		||||
 	mutex_lock(&flowtable_lock);
 | 
			
		||||
@@ -556,9 +623,58 @@ void nf_flow_table_free(struct nf_flowta
 | 
			
		||||
 	nf_flow_table_iterate(flow_table, nf_flow_table_do_cleanup, NULL);
 | 
			
		||||
 	nf_flow_table_iterate(flow_table, nf_flow_offload_gc_step, flow_table);
 | 
			
		||||
 	rhashtable_destroy(&flow_table->rhashtable);
 | 
			
		||||
+	if (flow_table->flags & NF_FLOWTABLE_F_HW)
 | 
			
		||||
+		nf_flow_offload_hw_free(flow_table);
 | 
			
		||||
 }
 | 
			
		||||
 EXPORT_SYMBOL_GPL(nf_flow_table_free);
 | 
			
		||||
 
 | 
			
		||||
+/* Must be called from user context. */
 | 
			
		||||
+void nf_flow_offload_hw_add(struct net *net, struct flow_offload *flow,
 | 
			
		||||
+			    struct nf_conn *ct)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct nf_flow_table_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	rcu_read_lock();
 | 
			
		||||
+	offload = rcu_dereference(nf_flow_table_hw_hook);
 | 
			
		||||
+	if (offload)
 | 
			
		||||
+		offload->add(net, flow, ct);
 | 
			
		||||
+	rcu_read_unlock();
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_add);
 | 
			
		||||
+
 | 
			
		||||
+/* Must be called from user context. */
 | 
			
		||||
+void nf_flow_offload_hw_del(struct net *net, struct flow_offload *flow)
 | 
			
		||||
+{
 | 
			
		||||
+	const struct nf_flow_table_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	rcu_read_lock();
 | 
			
		||||
+	offload = rcu_dereference(nf_flow_table_hw_hook);
 | 
			
		||||
+	if (offload)
 | 
			
		||||
+		offload->del(net, flow);
 | 
			
		||||
+	rcu_read_unlock();
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(nf_flow_offload_hw_del);
 | 
			
		||||
+
 | 
			
		||||
+int nf_flow_table_hw_register(const struct nf_flow_table_hw *offload)
 | 
			
		||||
+{
 | 
			
		||||
+	if (rcu_access_pointer(nf_flow_table_hw_hook))
 | 
			
		||||
+		return -EBUSY;
 | 
			
		||||
+
 | 
			
		||||
+	rcu_assign_pointer(nf_flow_table_hw_hook, offload);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(nf_flow_table_hw_register);
 | 
			
		||||
+
 | 
			
		||||
+void nf_flow_table_hw_unregister(const struct nf_flow_table_hw *offload)
 | 
			
		||||
+{
 | 
			
		||||
+	WARN_ON(rcu_access_pointer(nf_flow_table_hw_hook) != offload);
 | 
			
		||||
+	rcu_assign_pointer(nf_flow_table_hw_hook, NULL);
 | 
			
		||||
+
 | 
			
		||||
+	synchronize_rcu();
 | 
			
		||||
+}
 | 
			
		||||
+EXPORT_SYMBOL_GPL(nf_flow_table_hw_unregister);
 | 
			
		||||
+
 | 
			
		||||
 static int nf_flow_table_netdev_event(struct notifier_block *this,
 | 
			
		||||
 				      unsigned long event, void *ptr)
 | 
			
		||||
 {
 | 
			
		||||
--- /dev/null
 | 
			
		||||
+++ b/net/netfilter/nf_flow_table_hw.c
 | 
			
		||||
@@ -0,0 +1,169 @@
 | 
			
		||||
+#include <linux/kernel.h>
 | 
			
		||||
+#include <linux/init.h>
 | 
			
		||||
+#include <linux/module.h>
 | 
			
		||||
+#include <linux/netfilter.h>
 | 
			
		||||
+#include <linux/rhashtable.h>
 | 
			
		||||
+#include <linux/netdevice.h>
 | 
			
		||||
+#include <net/netfilter/nf_flow_table.h>
 | 
			
		||||
+#include <net/netfilter/nf_conntrack.h>
 | 
			
		||||
+#include <net/netfilter/nf_conntrack_core.h>
 | 
			
		||||
+#include <net/netfilter/nf_conntrack_tuple.h>
 | 
			
		||||
+
 | 
			
		||||
+static DEFINE_SPINLOCK(flow_offload_hw_pending_list_lock);
 | 
			
		||||
+static LIST_HEAD(flow_offload_hw_pending_list);
 | 
			
		||||
+
 | 
			
		||||
+static DEFINE_MUTEX(nf_flow_offload_hw_mutex);
 | 
			
		||||
+
 | 
			
		||||
+struct flow_offload_hw {
 | 
			
		||||
+	struct list_head	list;
 | 
			
		||||
+	enum flow_offload_type	type;
 | 
			
		||||
+	struct flow_offload	*flow;
 | 
			
		||||
+	struct nf_conn		*ct;
 | 
			
		||||
+	possible_net_t		flow_hw_net;
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static int do_flow_offload_hw(struct net *net, struct flow_offload *flow,
 | 
			
		||||
+			      int type)
 | 
			
		||||
+{
 | 
			
		||||
+	struct net_device *indev;
 | 
			
		||||
+	int ret, ifindex;
 | 
			
		||||
+
 | 
			
		||||
+	ifindex = flow->tuplehash[FLOW_OFFLOAD_DIR_ORIGINAL].tuple.iifidx;
 | 
			
		||||
+	indev = dev_get_by_index(net, ifindex);
 | 
			
		||||
+	if (WARN_ON(!indev))
 | 
			
		||||
+		return 0;
 | 
			
		||||
+
 | 
			
		||||
+	mutex_lock(&nf_flow_offload_hw_mutex);
 | 
			
		||||
+	ret = indev->netdev_ops->ndo_flow_offload(type, flow);
 | 
			
		||||
+	mutex_unlock(&nf_flow_offload_hw_mutex);
 | 
			
		||||
+
 | 
			
		||||
+	dev_put(indev);
 | 
			
		||||
+
 | 
			
		||||
+	return ret;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_hw_work_add(struct flow_offload_hw *offload)
 | 
			
		||||
+{
 | 
			
		||||
+	struct net *net;
 | 
			
		||||
+	int ret;
 | 
			
		||||
+
 | 
			
		||||
+	if (nf_ct_is_dying(offload->ct))
 | 
			
		||||
+		return;
 | 
			
		||||
+
 | 
			
		||||
+	net = read_pnet(&offload->flow_hw_net);
 | 
			
		||||
+	ret = do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_ADD);
 | 
			
		||||
+	if (ret >= 0)
 | 
			
		||||
+		offload->flow->flags |= FLOW_OFFLOAD_HW;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_hw_work_del(struct flow_offload_hw *offload)
 | 
			
		||||
+{
 | 
			
		||||
+	struct net *net = read_pnet(&offload->flow_hw_net);
 | 
			
		||||
+
 | 
			
		||||
+	do_flow_offload_hw(net, offload->flow, FLOW_OFFLOAD_DEL);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_hw_work(struct work_struct *work)
 | 
			
		||||
+{
 | 
			
		||||
+	struct flow_offload_hw *offload, *next;
 | 
			
		||||
+	LIST_HEAD(hw_offload_pending);
 | 
			
		||||
+
 | 
			
		||||
+	spin_lock_bh(&flow_offload_hw_pending_list_lock);
 | 
			
		||||
+	list_replace_init(&flow_offload_hw_pending_list, &hw_offload_pending);
 | 
			
		||||
+	spin_unlock_bh(&flow_offload_hw_pending_list_lock);
 | 
			
		||||
+
 | 
			
		||||
+	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
 | 
			
		||||
+		switch (offload->type) {
 | 
			
		||||
+		case FLOW_OFFLOAD_ADD:
 | 
			
		||||
+			flow_offload_hw_work_add(offload);
 | 
			
		||||
+			break;
 | 
			
		||||
+		case FLOW_OFFLOAD_DEL:
 | 
			
		||||
+			flow_offload_hw_work_del(offload);
 | 
			
		||||
+			break;
 | 
			
		||||
+		}
 | 
			
		||||
+		if (offload->ct)
 | 
			
		||||
+			nf_conntrack_put(&offload->ct->ct_general);
 | 
			
		||||
+		list_del(&offload->list);
 | 
			
		||||
+		kfree(offload);
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_queue_work(struct flow_offload_hw *offload)
 | 
			
		||||
+{
 | 
			
		||||
+	spin_lock_bh(&flow_offload_hw_pending_list_lock);
 | 
			
		||||
+	list_add_tail(&offload->list, &flow_offload_hw_pending_list);
 | 
			
		||||
+	spin_unlock_bh(&flow_offload_hw_pending_list_lock);
 | 
			
		||||
+
 | 
			
		||||
+	schedule_work(&nf_flow_offload_hw_work);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_hw_add(struct net *net, struct flow_offload *flow,
 | 
			
		||||
+				struct nf_conn *ct)
 | 
			
		||||
+{
 | 
			
		||||
+	struct flow_offload_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
 | 
			
		||||
+	if (!offload)
 | 
			
		||||
+		return;
 | 
			
		||||
+
 | 
			
		||||
+	nf_conntrack_get(&ct->ct_general);
 | 
			
		||||
+	offload->type = FLOW_OFFLOAD_ADD;
 | 
			
		||||
+	offload->ct = ct;
 | 
			
		||||
+	offload->flow = flow;
 | 
			
		||||
+	write_pnet(&offload->flow_hw_net, net);
 | 
			
		||||
+
 | 
			
		||||
+	flow_offload_queue_work(offload);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void flow_offload_hw_del(struct net *net, struct flow_offload *flow)
 | 
			
		||||
+{
 | 
			
		||||
+	struct flow_offload_hw *offload;
 | 
			
		||||
+
 | 
			
		||||
+	offload = kmalloc(sizeof(struct flow_offload_hw), GFP_ATOMIC);
 | 
			
		||||
+	if (!offload)
 | 
			
		||||
+		return;
 | 
			
		||||
+
 | 
			
		||||
+	offload->type = FLOW_OFFLOAD_DEL;
 | 
			
		||||
+	offload->ct = NULL;
 | 
			
		||||
+	offload->flow = flow;
 | 
			
		||||
+	write_pnet(&offload->flow_hw_net, net);
 | 
			
		||||
+
 | 
			
		||||
+	flow_offload_queue_work(offload);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static const struct nf_flow_table_hw flow_offload_hw = {
 | 
			
		||||
+	.add	= flow_offload_hw_add,
 | 
			
		||||
+	.del	= flow_offload_hw_del,
 | 
			
		||||
+	.owner	= THIS_MODULE,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static int __init nf_flow_table_hw_module_init(void)
 | 
			
		||||
+{
 | 
			
		||||
+	INIT_WORK(&nf_flow_offload_hw_work, flow_offload_hw_work);
 | 
			
		||||
+	nf_flow_table_hw_register(&flow_offload_hw);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void __exit nf_flow_table_hw_module_exit(void)
 | 
			
		||||
+{
 | 
			
		||||
+	struct flow_offload_hw *offload, *next;
 | 
			
		||||
+	LIST_HEAD(hw_offload_pending);
 | 
			
		||||
+
 | 
			
		||||
+	nf_flow_table_hw_unregister(&flow_offload_hw);
 | 
			
		||||
+	cancel_work_sync(&nf_flow_offload_hw_work);
 | 
			
		||||
+
 | 
			
		||||
+	list_for_each_entry_safe(offload, next, &hw_offload_pending, list) {
 | 
			
		||||
+		if (offload->ct)
 | 
			
		||||
+			nf_conntrack_put(&offload->ct->ct_general);
 | 
			
		||||
+		list_del(&offload->list);
 | 
			
		||||
+		kfree(offload);
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+module_init(nf_flow_table_hw_module_init);
 | 
			
		||||
+module_exit(nf_flow_table_hw_module_exit);
 | 
			
		||||
+
 | 
			
		||||
+MODULE_LICENSE("GPL");
 | 
			
		||||
+MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>");
 | 
			
		||||
+MODULE_ALIAS("nf-flow-table-hw");
 | 
			
		||||
--- a/net/netfilter/nf_tables_api.c
 | 
			
		||||
+++ b/net/netfilter/nf_tables_api.c
 | 
			
		||||
@@ -5743,6 +5743,13 @@ static int nf_tables_flowtable_parse_hoo
 | 
			
		||||
 	if (err < 0)
 | 
			
		||||
 		return err;
 | 
			
		||||
 
 | 
			
		||||
+	for (i = 0; i < n; i++) {
 | 
			
		||||
+		if (flowtable->data.flags & NF_FLOWTABLE_F_HW &&
 | 
			
		||||
+		    !dev_array[i]->netdev_ops->ndo_flow_offload) {
 | 
			
		||||
+			return -EOPNOTSUPP;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	ops = kcalloc(n, sizeof(struct nf_hook_ops), GFP_KERNEL);
 | 
			
		||||
 	if (!ops)
 | 
			
		||||
 		return -ENOMEM;
 | 
			
		||||
@@ -5873,10 +5880,19 @@ static int nf_tables_newflowtable(struct
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
 	flowtable->data.type = type;
 | 
			
		||||
+	write_pnet(&flowtable->data.ft_net, net);
 | 
			
		||||
+
 | 
			
		||||
 	err = type->init(&flowtable->data);
 | 
			
		||||
 	if (err < 0)
 | 
			
		||||
 		goto err3;
 | 
			
		||||
 
 | 
			
		||||
+	if (nla[NFTA_FLOWTABLE_FLAGS]) {
 | 
			
		||||
+		flowtable->data.flags =
 | 
			
		||||
+			ntohl(nla_get_be32(nla[NFTA_FLOWTABLE_FLAGS]));
 | 
			
		||||
+		if (flowtable->data.flags & ~NF_FLOWTABLE_F_HW)
 | 
			
		||||
+			goto err4;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	err = nf_tables_flowtable_parse_hook(&ctx, nla[NFTA_FLOWTABLE_HOOK],
 | 
			
		||||
 					     flowtable);
 | 
			
		||||
 	if (err < 0)
 | 
			
		||||
@@ -6002,7 +6018,8 @@ static int nf_tables_fill_flowtable_info
 | 
			
		||||
 	    nla_put_string(skb, NFTA_FLOWTABLE_NAME, flowtable->name) ||
 | 
			
		||||
 	    nla_put_be32(skb, NFTA_FLOWTABLE_USE, htonl(flowtable->use)) ||
 | 
			
		||||
 	    nla_put_be64(skb, NFTA_FLOWTABLE_HANDLE, cpu_to_be64(flowtable->handle),
 | 
			
		||||
-			 NFTA_FLOWTABLE_PAD))
 | 
			
		||||
+			 NFTA_FLOWTABLE_PAD) ||
 | 
			
		||||
+	    nla_put_be32(skb, NFTA_FLOWTABLE_FLAGS, htonl(flowtable->data.flags)))
 | 
			
		||||
 		goto nla_put_failure;
 | 
			
		||||
 
 | 
			
		||||
 	nest = nla_nest_start_noflag(skb, NFTA_FLOWTABLE_HOOK);
 | 
			
		||||
--- a/net/netfilter/nft_flow_offload.c
 | 
			
		||||
+++ b/net/netfilter/nft_flow_offload.c
 | 
			
		||||
@@ -128,6 +128,9 @@ static void nft_flow_offload_eval(const
 | 
			
		||||
 	if (ret < 0)
 | 
			
		||||
 		goto err_flow_add;
 | 
			
		||||
 
 | 
			
		||||
+	if (flowtable->flags & NF_FLOWTABLE_F_HW)
 | 
			
		||||
+		nf_flow_offload_hw_add(nft_net(pkt), flow, ct);
 | 
			
		||||
+
 | 
			
		||||
 	dst_release(route.tuple[!dir].dst);
 | 
			
		||||
 	return;
 | 
			
		||||
 
 | 
			
		||||
| 
						 | 
				
			
			@ -1,344 +0,0 @@
 | 
			
		|||
From: Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
Date: Sun, 26 Jul 2020 14:03:21 +0200
 | 
			
		||||
Subject: [PATCH] net: add support for threaded NAPI polling
 | 
			
		||||
 | 
			
		||||
For some drivers (especially 802.11 drivers), doing a lot of work in the NAPI
 | 
			
		||||
poll function does not perform well. Since NAPI poll is bound to the CPU it
 | 
			
		||||
was scheduled from, we can easily end up with a few very busy CPUs spending
 | 
			
		||||
most of their time in softirq/ksoftirqd and some idle ones.
 | 
			
		||||
 | 
			
		||||
Introduce threaded NAPI for such drivers based on a workqueue. The API is the
 | 
			
		||||
same except for using netif_threaded_napi_add instead of netif_napi_add.
 | 
			
		||||
 | 
			
		||||
In my tests with mt76 on MT7621 using threaded NAPI + a thread for tx scheduling
 | 
			
		||||
improves LAN->WLAN bridging throughput by 10-50%. Throughput without threaded
 | 
			
		||||
NAPI is wildly inconsistent, depending on the CPU that runs the tx scheduling
 | 
			
		||||
thread.
 | 
			
		||||
 | 
			
		||||
With threaded NAPI it seems stable and consistent (and higher than the best
 | 
			
		||||
results I got without it).
 | 
			
		||||
 | 
			
		||||
Based on a patch by Hillf Danton
 | 
			
		||||
 | 
			
		||||
Cc: Hillf Danton <hdanton@sina.com>
 | 
			
		||||
Signed-off-by: Felix Fietkau <nbd@nbd.name>
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
--- a/include/linux/netdevice.h
 | 
			
		||||
+++ b/include/linux/netdevice.h
 | 
			
		||||
@@ -340,6 +340,7 @@ struct napi_struct {
 | 
			
		||||
 	struct list_head	dev_list;
 | 
			
		||||
 	struct hlist_node	napi_hash_node;
 | 
			
		||||
 	unsigned int		napi_id;
 | 
			
		||||
+	struct work_struct	work;
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 enum {
 | 
			
		||||
@@ -350,6 +351,7 @@ enum {
 | 
			
		||||
 	NAPI_STATE_HASHED,	/* In NAPI hash (busy polling possible) */
 | 
			
		||||
 	NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */
 | 
			
		||||
 	NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */
 | 
			
		||||
+	NAPI_STATE_THREADED,	/* Use threaded NAPI */
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 enum {
 | 
			
		||||
@@ -360,6 +362,7 @@ enum {
 | 
			
		||||
 	NAPIF_STATE_HASHED	 = BIT(NAPI_STATE_HASHED),
 | 
			
		||||
 	NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL),
 | 
			
		||||
 	NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL),
 | 
			
		||||
+	NAPIF_STATE_THREADED	 = BIT(NAPI_STATE_THREADED),
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 enum gro_result {
 | 
			
		||||
@@ -2101,6 +2104,7 @@ struct net_device {
 | 
			
		||||
 	struct lock_class_key	addr_list_lock_key;
 | 
			
		||||
 	bool			proto_down;
 | 
			
		||||
 	unsigned		wol_enabled:1;
 | 
			
		||||
+	unsigned		threaded:1;
 | 
			
		||||
 };
 | 
			
		||||
 #define to_net_dev(d) container_of(d, struct net_device, dev)
 | 
			
		||||
 
 | 
			
		||||
@@ -2281,6 +2285,26 @@ void netif_napi_add(struct net_device *d
 | 
			
		||||
 		    int (*poll)(struct napi_struct *, int), int weight);
 | 
			
		||||
 
 | 
			
		||||
 /**
 | 
			
		||||
+ *	netif_threaded_napi_add - initialize a NAPI context
 | 
			
		||||
+ *	@dev:  network device
 | 
			
		||||
+ *	@napi: NAPI context
 | 
			
		||||
+ *	@poll: polling function
 | 
			
		||||
+ *	@weight: default weight
 | 
			
		||||
+ *
 | 
			
		||||
+ * This variant of netif_napi_add() should be used from drivers using NAPI
 | 
			
		||||
+ * with CPU intensive poll functions.
 | 
			
		||||
+ * This will schedule polling from a high priority workqueue
 | 
			
		||||
+ */
 | 
			
		||||
+static inline void netif_threaded_napi_add(struct net_device *dev,
 | 
			
		||||
+					   struct napi_struct *napi,
 | 
			
		||||
+					   int (*poll)(struct napi_struct *, int),
 | 
			
		||||
+					   int weight)
 | 
			
		||||
+{
 | 
			
		||||
+	set_bit(NAPI_STATE_THREADED, &napi->state);
 | 
			
		||||
+	netif_napi_add(dev, napi, poll, weight);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+/**
 | 
			
		||||
  *	netif_tx_napi_add - initialize a NAPI context
 | 
			
		||||
  *	@dev:  network device
 | 
			
		||||
  *	@napi: NAPI context
 | 
			
		||||
--- a/net/core/dev.c
 | 
			
		||||
+++ b/net/core/dev.c
 | 
			
		||||
@@ -156,6 +156,7 @@ static DEFINE_SPINLOCK(offload_lock);
 | 
			
		||||
 struct list_head ptype_base[PTYPE_HASH_SIZE] __read_mostly;
 | 
			
		||||
 struct list_head ptype_all __read_mostly;	/* Taps */
 | 
			
		||||
 static struct list_head offload_base __read_mostly;
 | 
			
		||||
+static struct workqueue_struct *napi_workq __read_mostly;
 | 
			
		||||
 
 | 
			
		||||
 static int netif_rx_internal(struct sk_buff *skb);
 | 
			
		||||
 static int call_netdevice_notifiers_info(unsigned long val,
 | 
			
		||||
@@ -5931,6 +5932,11 @@ void __napi_schedule(struct napi_struct
 | 
			
		||||
 {
 | 
			
		||||
 	unsigned long flags;
 | 
			
		||||
 
 | 
			
		||||
+	if (test_bit(NAPI_STATE_THREADED, &n->state)) {
 | 
			
		||||
+		queue_work(napi_workq, &n->work);
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
 	local_irq_save(flags);
 | 
			
		||||
 	____napi_schedule(this_cpu_ptr(&softnet_data), n);
 | 
			
		||||
 	local_irq_restore(flags);
 | 
			
		||||
@@ -6246,9 +6256,89 @@ static void init_gro_hash(struct napi_st
 | 
			
		||||
 	napi->gro_bitmask = 0;
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+static int __napi_poll(struct napi_struct *n, bool *repoll)
 | 
			
		||||
+{
 | 
			
		||||
+	int work, weight;
 | 
			
		||||
+
 | 
			
		||||
+	weight = n->weight;
 | 
			
		||||
+
 | 
			
		||||
+	/* This NAPI_STATE_SCHED test is for avoiding a race
 | 
			
		||||
+	 * with netpoll's poll_napi().  Only the entity which
 | 
			
		||||
+	 * obtains the lock and sees NAPI_STATE_SCHED set will
 | 
			
		||||
+	 * actually make the ->poll() call.  Therefore we avoid
 | 
			
		||||
+	 * accidentally calling ->poll() when NAPI is not scheduled.
 | 
			
		||||
+	 */
 | 
			
		||||
+	work = 0;
 | 
			
		||||
+	if (test_bit(NAPI_STATE_SCHED, &n->state)) {
 | 
			
		||||
+		work = n->poll(n, weight);
 | 
			
		||||
+		trace_napi_poll(n, work, weight);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	WARN_ON_ONCE(work > weight);
 | 
			
		||||
+
 | 
			
		||||
+	if (likely(work < weight))
 | 
			
		||||
+		return work;
 | 
			
		||||
+
 | 
			
		||||
+	/* Drivers must not modify the NAPI state if they
 | 
			
		||||
+	 * consume the entire weight.  In such cases this code
 | 
			
		||||
+	 * still "owns" the NAPI instance and therefore can
 | 
			
		||||
+	 * move the instance around on the list at-will.
 | 
			
		||||
+	 */
 | 
			
		||||
+	if (unlikely(napi_disable_pending(n))) {
 | 
			
		||||
+		napi_complete(n);
 | 
			
		||||
+		return work;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	if (n->gro_bitmask) {
 | 
			
		||||
+		/* flush too old packets
 | 
			
		||||
+		 * If HZ < 1000, flush all packets.
 | 
			
		||||
+		 */
 | 
			
		||||
+		napi_gro_flush(n, HZ >= 1000);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	gro_normal_list(n);
 | 
			
		||||
+
 | 
			
		||||
+	*repoll = true;
 | 
			
		||||
+
 | 
			
		||||
+	return work;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static void napi_workfn(struct work_struct *work)
 | 
			
		||||
+{
 | 
			
		||||
+	struct napi_struct *n = container_of(work, struct napi_struct, work);
 | 
			
		||||
+	void *have;
 | 
			
		||||
+
 | 
			
		||||
+	for (;;) {
 | 
			
		||||
+		bool repoll = false;
 | 
			
		||||
+
 | 
			
		||||
+		local_bh_disable();
 | 
			
		||||
+
 | 
			
		||||
+		have = netpoll_poll_lock(n);
 | 
			
		||||
+		__napi_poll(n, &repoll);
 | 
			
		||||
+		netpoll_poll_unlock(have);
 | 
			
		||||
+
 | 
			
		||||
+		local_bh_enable();
 | 
			
		||||
+
 | 
			
		||||
+		if (!repoll)
 | 
			
		||||
+			return;
 | 
			
		||||
+
 | 
			
		||||
+		if (!need_resched())
 | 
			
		||||
+			continue;
 | 
			
		||||
+
 | 
			
		||||
+		/*
 | 
			
		||||
+		 * have to pay for the latency of task switch even if
 | 
			
		||||
+		 * napi is scheduled
 | 
			
		||||
+		 */
 | 
			
		||||
+		queue_work(napi_workq, work);
 | 
			
		||||
+		return;
 | 
			
		||||
+	}
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 void netif_napi_add(struct net_device *dev, struct napi_struct *napi,
 | 
			
		||||
 		    int (*poll)(struct napi_struct *, int), int weight)
 | 
			
		||||
 {
 | 
			
		||||
+	if (dev->threaded)
 | 
			
		||||
+		set_bit(NAPI_STATE_THREADED, &napi->state);
 | 
			
		||||
 	INIT_LIST_HEAD(&napi->poll_list);
 | 
			
		||||
 	hrtimer_init(&napi->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
 | 
			
		||||
 	napi->timer.function = napi_watchdog;
 | 
			
		||||
@@ -6265,6 +6355,7 @@ void netif_napi_add(struct net_device *d
 | 
			
		||||
 #ifdef CONFIG_NETPOLL
 | 
			
		||||
 	napi->poll_owner = -1;
 | 
			
		||||
 #endif
 | 
			
		||||
+	INIT_WORK(&napi->work, napi_workfn);
 | 
			
		||||
 	set_bit(NAPI_STATE_SCHED, &napi->state);
 | 
			
		||||
 	set_bit(NAPI_STATE_NPSVC, &napi->state);
 | 
			
		||||
 	list_add_rcu(&napi->dev_list, &dev->napi_list);
 | 
			
		||||
@@ -6305,6 +6396,7 @@ static void flush_gro_hash(struct napi_s
 | 
			
		||||
 void netif_napi_del(struct napi_struct *napi)
 | 
			
		||||
 {
 | 
			
		||||
 	might_sleep();
 | 
			
		||||
+	cancel_work_sync(&napi->work);
 | 
			
		||||
 	if (napi_hash_del(napi))
 | 
			
		||||
 		synchronize_net();
 | 
			
		||||
 	list_del_init(&napi->dev_list);
 | 
			
		||||
@@ -6317,50 +6409,18 @@ EXPORT_SYMBOL(netif_napi_del);
 | 
			
		||||
 
 | 
			
		||||
 static int napi_poll(struct napi_struct *n, struct list_head *repoll)
 | 
			
		||||
 {
 | 
			
		||||
+	bool do_repoll = false;
 | 
			
		||||
 	void *have;
 | 
			
		||||
-	int work, weight;
 | 
			
		||||
+	int work;
 | 
			
		||||
 
 | 
			
		||||
 	list_del_init(&n->poll_list);
 | 
			
		||||
 
 | 
			
		||||
 	have = netpoll_poll_lock(n);
 | 
			
		||||
 
 | 
			
		||||
-	weight = n->weight;
 | 
			
		||||
-
 | 
			
		||||
-	/* This NAPI_STATE_SCHED test is for avoiding a race
 | 
			
		||||
-	 * with netpoll's poll_napi().  Only the entity which
 | 
			
		||||
-	 * obtains the lock and sees NAPI_STATE_SCHED set will
 | 
			
		||||
-	 * actually make the ->poll() call.  Therefore we avoid
 | 
			
		||||
-	 * accidentally calling ->poll() when NAPI is not scheduled.
 | 
			
		||||
-	 */
 | 
			
		||||
-	work = 0;
 | 
			
		||||
-	if (test_bit(NAPI_STATE_SCHED, &n->state)) {
 | 
			
		||||
-		work = n->poll(n, weight);
 | 
			
		||||
-		trace_napi_poll(n, work, weight);
 | 
			
		||||
-	}
 | 
			
		||||
-
 | 
			
		||||
-	WARN_ON_ONCE(work > weight);
 | 
			
		||||
+	work = __napi_poll(n, &do_repoll);
 | 
			
		||||
 
 | 
			
		||||
-	if (likely(work < weight))
 | 
			
		||||
-		goto out_unlock;
 | 
			
		||||
-
 | 
			
		||||
-	/* Drivers must not modify the NAPI state if they
 | 
			
		||||
-	 * consume the entire weight.  In such cases this code
 | 
			
		||||
-	 * still "owns" the NAPI instance and therefore can
 | 
			
		||||
-	 * move the instance around on the list at-will.
 | 
			
		||||
-	 */
 | 
			
		||||
-	if (unlikely(napi_disable_pending(n))) {
 | 
			
		||||
-		napi_complete(n);
 | 
			
		||||
+	if (!do_repoll)
 | 
			
		||||
 		goto out_unlock;
 | 
			
		||||
-	}
 | 
			
		||||
-
 | 
			
		||||
-	if (n->gro_bitmask) {
 | 
			
		||||
-		/* flush too old packets
 | 
			
		||||
-		 * If HZ < 1000, flush all packets.
 | 
			
		||||
-		 */
 | 
			
		||||
-		napi_gro_flush(n, HZ >= 1000);
 | 
			
		||||
-	}
 | 
			
		||||
-
 | 
			
		||||
-	gro_normal_list(n);
 | 
			
		||||
 
 | 
			
		||||
 	/* Some drivers may have called napi_schedule
 | 
			
		||||
 	 * prior to exhausting their budget.
 | 
			
		||||
@@ -10340,6 +10400,10 @@ static int __init net_dev_init(void)
 | 
			
		||||
 		sd->backlog.weight = weight_p;
 | 
			
		||||
 	}
 | 
			
		||||
 
 | 
			
		||||
+	napi_workq = alloc_workqueue("napi_workq", WQ_UNBOUND | WQ_HIGHPRI,
 | 
			
		||||
+				     WQ_UNBOUND_MAX_ACTIVE | WQ_SYSFS);
 | 
			
		||||
+	BUG_ON(!napi_workq);
 | 
			
		||||
+
 | 
			
		||||
 	dev_boot_phase = 0;
 | 
			
		||||
 
 | 
			
		||||
 	/* The loopback device is special if any other network devices
 | 
			
		||||
--- a/net/core/net-sysfs.c
 | 
			
		||||
+++ b/net/core/net-sysfs.c
 | 
			
		||||
@@ -442,6 +442,52 @@ static ssize_t proto_down_store(struct d
 | 
			
		||||
 }
 | 
			
		||||
 NETDEVICE_SHOW_RW(proto_down, fmt_dec);
 | 
			
		||||
 
 | 
			
		||||
+static int change_napi_threaded(struct net_device *dev, unsigned long val)
 | 
			
		||||
+{
 | 
			
		||||
+	struct napi_struct *napi;
 | 
			
		||||
+
 | 
			
		||||
+	if (list_empty(&dev->napi_list))
 | 
			
		||||
+		return -EOPNOTSUPP;
 | 
			
		||||
+
 | 
			
		||||
+	list_for_each_entry(napi, &dev->napi_list, dev_list) {
 | 
			
		||||
+		if (val)
 | 
			
		||||
+			set_bit(NAPI_STATE_THREADED, &napi->state);
 | 
			
		||||
+		else
 | 
			
		||||
+			clear_bit(NAPI_STATE_THREADED, &napi->state);
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static ssize_t napi_threaded_store(struct device *dev,
 | 
			
		||||
+				struct device_attribute *attr,
 | 
			
		||||
+				const char *buf, size_t len)
 | 
			
		||||
+{
 | 
			
		||||
+	return netdev_store(dev, attr, buf, len, change_napi_threaded);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static ssize_t napi_threaded_show(struct device *dev,
 | 
			
		||||
+				  struct device_attribute *attr,
 | 
			
		||||
+				  char *buf)
 | 
			
		||||
+{
 | 
			
		||||
+	struct net_device *netdev = to_net_dev(dev);
 | 
			
		||||
+	struct napi_struct *napi;
 | 
			
		||||
+	bool enabled = false;
 | 
			
		||||
+
 | 
			
		||||
+	if (!rtnl_trylock())
 | 
			
		||||
+		return restart_syscall();
 | 
			
		||||
+
 | 
			
		||||
+	list_for_each_entry(napi, &netdev->napi_list, dev_list) {
 | 
			
		||||
+		if (test_bit(NAPI_STATE_THREADED, &napi->state))
 | 
			
		||||
+			enabled = true;
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	rtnl_unlock();
 | 
			
		||||
+
 | 
			
		||||
+	return sprintf(buf, fmt_dec, enabled);
 | 
			
		||||
+}
 | 
			
		||||
+static DEVICE_ATTR_RW(napi_threaded);
 | 
			
		||||
+
 | 
			
		||||
 static ssize_t phys_port_id_show(struct device *dev,
 | 
			
		||||
 				 struct device_attribute *attr, char *buf)
 | 
			
		||||
 {
 | 
			
		||||
@@ -532,6 +578,7 @@ static struct attribute *net_class_attrs
 | 
			
		||||
 	&dev_attr_flags.attr,
 | 
			
		||||
 	&dev_attr_tx_queue_len.attr,
 | 
			
		||||
 	&dev_attr_gro_flush_timeout.attr,
 | 
			
		||||
+	&dev_attr_napi_threaded.attr,
 | 
			
		||||
 	&dev_attr_phys_port_id.attr,
 | 
			
		||||
 	&dev_attr_phys_port_name.attr,
 | 
			
		||||
 	&dev_attr_phys_switch_id.attr,
 | 
			
		||||
| 
						 | 
				
			
			@ -1,57 +0,0 @@
 | 
			
		|||
From 63e4b45c82ed1bde979da7052229a4229ce9cabf Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: Georgi Valkov <gvalkov@abv.bg>
 | 
			
		||||
Date: Tue, 1 Feb 2022 08:16:18 +0100
 | 
			
		||||
Subject: ipheth: fix EOVERFLOW in ipheth_rcvbulk_callback
 | 
			
		||||
 | 
			
		||||
When rx_buf is allocated we need to account for IPHETH_IP_ALIGN,
 | 
			
		||||
which reduces the usable size by 2 bytes. Otherwise we have 1512
 | 
			
		||||
bytes usable instead of 1514, and if we receive more than 1512
 | 
			
		||||
bytes, ipheth_rcvbulk_callback is called with status -EOVERFLOW,
 | 
			
		||||
after which the driver malfunctiones and all communication stops.
 | 
			
		||||
 | 
			
		||||
Resolves ipheth 2-1:4.2: ipheth_rcvbulk_callback: urb status: -75
 | 
			
		||||
 | 
			
		||||
Fixes: f33d9e2b48a3 ("usbnet: ipheth: fix connectivity with iOS 14")
 | 
			
		||||
Signed-off-by: Georgi Valkov <gvalkov@abv.bg>
 | 
			
		||||
Tested-by: Jan Kiszka <jan.kiszka@siemens.com>
 | 
			
		||||
Link: https://lore.kernel.org/all/B60B8A4B-92A0-49B3-805D-809A2433B46C@abv.bg/
 | 
			
		||||
Link: https://lore.kernel.org/all/24851bd2769434a5fc24730dce8e8a984c5a4505.1643699778.git.jan.kiszka@siemens.com/
 | 
			
		||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
 | 
			
		||||
---
 | 
			
		||||
 drivers/net/usb/ipheth.c | 6 +++---
 | 
			
		||||
 1 file changed, 3 insertions(+), 3 deletions(-)
 | 
			
		||||
 | 
			
		||||
diff --git a/drivers/net/usb/ipheth.c b/drivers/net/usb/ipheth.c
 | 
			
		||||
index cd33955df0b65f..6a769df0b4213c 100644
 | 
			
		||||
--- a/drivers/net/usb/ipheth.c
 | 
			
		||||
+++ b/drivers/net/usb/ipheth.c
 | 
			
		||||
@@ -121,7 +121,7 @@ static int ipheth_alloc_urbs(struct ipheth_device *iphone)
 | 
			
		||||
 	if (tx_buf == NULL)
 | 
			
		||||
 		goto free_rx_urb;
 | 
			
		||||
 
 | 
			
		||||
-	rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE,
 | 
			
		||||
+	rx_buf = usb_alloc_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN,
 | 
			
		||||
 				    GFP_KERNEL, &rx_urb->transfer_dma);
 | 
			
		||||
 	if (rx_buf == NULL)
 | 
			
		||||
 		goto free_tx_buf;
 | 
			
		||||
@@ -146,7 +146,7 @@ error_nomem:
 | 
			
		||||
 
 | 
			
		||||
 static void ipheth_free_urbs(struct ipheth_device *iphone)
 | 
			
		||||
 {
 | 
			
		||||
-	usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->rx_buf,
 | 
			
		||||
+	usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN, iphone->rx_buf,
 | 
			
		||||
 			  iphone->rx_urb->transfer_dma);
 | 
			
		||||
 	usb_free_coherent(iphone->udev, IPHETH_BUF_SIZE, iphone->tx_buf,
 | 
			
		||||
 			  iphone->tx_urb->transfer_dma);
 | 
			
		||||
@@ -317,7 +317,7 @@ static int ipheth_rx_submit(struct ipheth_device *dev, gfp_t mem_flags)
 | 
			
		||||
 
 | 
			
		||||
 	usb_fill_bulk_urb(dev->rx_urb, udev,
 | 
			
		||||
 			  usb_rcvbulkpipe(udev, dev->bulk_in),
 | 
			
		||||
-			  dev->rx_buf, IPHETH_BUF_SIZE,
 | 
			
		||||
+			  dev->rx_buf, IPHETH_BUF_SIZE + IPHETH_IP_ALIGN,
 | 
			
		||||
 			  ipheth_rcvbulk_callback,
 | 
			
		||||
 			  dev);
 | 
			
		||||
 	dev->rx_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 | 
			
		||||
-- 
 | 
			
		||||
cgit 
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,30 +0,0 @@
 | 
			
		|||
#!/bin/sh /etc/rc.common
 | 
			
		||||
# (C) 2013 openwrt.org
 | 
			
		||||
 | 
			
		||||
START=81
 | 
			
		||||
 | 
			
		||||
boot() {
 | 
			
		||||
	SHA_ALGS="sha1-neon sha224-neon sha256-neon sha384-neon sha512-neon md5-generic"
 | 
			
		||||
 | 
			
		||||
	for alg in $SHA_ALGS; do
 | 
			
		||||
		crconf add driver "authenc(hmac($alg),cbc(aes-generic))" type 3
 | 
			
		||||
		crconf add driver "pcrypt(authenc(hmac($alg),cbc(aes-generic)))" type 3
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	for alg in $SHA_ALGS; do
 | 
			
		||||
		crconf add driver "authenc(hmac($alg),cbc(des3_ede-generic))" type 3
 | 
			
		||||
		crconf add driver "pcrypt(authenc(hmac($alg),cbc(des3_ede-generic)))" type 3
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
start() {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
restart() {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
stop() {
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,171 +0,0 @@
 | 
			
		|||
# Copyright (C) 2019 OpenWrt.org
 | 
			
		||||
 | 
			
		||||
. /lib/functions.sh
 | 
			
		||||
. /lib/functions/system.sh
 | 
			
		||||
 | 
			
		||||
caldata_dd() {
 | 
			
		||||
	local source=$1
 | 
			
		||||
	local target=$2
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local offset=$(($4))
 | 
			
		||||
 | 
			
		||||
	dd if=$source of=$target iflag=skip_bytes,fullblock bs=$count skip=$offset count=1 2>/dev/null
 | 
			
		||||
	return $?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_die() {
 | 
			
		||||
	echo "caldata: " "$*"
 | 
			
		||||
	exit 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_extract() {
 | 
			
		||||
	local part=$1
 | 
			
		||||
	local offset=$(($2))
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local mtd
 | 
			
		||||
 | 
			
		||||
	mtd=$(find_mtd_chardev $part)
 | 
			
		||||
	[ -n "$mtd" ] || caldata_die "no mtd device found for partition $part"
 | 
			
		||||
 | 
			
		||||
	caldata_dd $mtd /lib/firmware/$FIRMWARE $count $offset || \
 | 
			
		||||
		caldata_die "failed to extract calibration data from $mtd"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_extract_ubi() {
 | 
			
		||||
	local part=$1
 | 
			
		||||
	local offset=$(($2))
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local ubidev
 | 
			
		||||
	local ubi
 | 
			
		||||
 | 
			
		||||
	. /lib/upgrade/nand.sh
 | 
			
		||||
 | 
			
		||||
	ubidev=$(nand_find_ubi $CI_UBIPART)
 | 
			
		||||
	ubi=$(nand_find_volume $ubidev $part)
 | 
			
		||||
	[ -n "$ubi" ] || caldata_die "no UBI volume found for $part"
 | 
			
		||||
 | 
			
		||||
	caldata_dd /dev/$ubi /lib/firmware/$FIRMWARE $count $offset || \
 | 
			
		||||
		caldata_die "failed to extract calibration data from $ubi"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_extract_reverse() {
 | 
			
		||||
	local part=$1
 | 
			
		||||
	local offset=$2
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local mtd
 | 
			
		||||
	local reversed
 | 
			
		||||
	local caldata
 | 
			
		||||
 | 
			
		||||
	mtd=$(find_mtd_chardev "$part")
 | 
			
		||||
	reversed=$(hexdump -v -s $offset -n $count -e '/1 "%02x "' $mtd)
 | 
			
		||||
 | 
			
		||||
	for byte in $reversed; do
 | 
			
		||||
		caldata="\x${byte}${caldata}"
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	printf "%b" "$caldata" > /lib/firmware/$FIRMWARE
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_from_file() {
 | 
			
		||||
	local source=$1
 | 
			
		||||
	local offset=$(($2))
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local target=$4
 | 
			
		||||
 | 
			
		||||
	[ -n "$target" ] || target=/lib/firmware/$FIRMWARE
 | 
			
		||||
 | 
			
		||||
	caldata_dd $source $target $count $offset || \
 | 
			
		||||
		caldata_die "failed to extract calibration data from $source"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_sysfsload_from_file() {
 | 
			
		||||
	local source=$1
 | 
			
		||||
	local offset=$(($2))
 | 
			
		||||
	local count=$(($3))
 | 
			
		||||
	local target_dir="/sys/$DEVPATH"
 | 
			
		||||
	local target="$target_dir/data"
 | 
			
		||||
 | 
			
		||||
	[ -d "$target_dir" ] || \
 | 
			
		||||
		caldata_die "no sysfs dir to write: $target"
 | 
			
		||||
 | 
			
		||||
	echo 1 > "$target_dir/loading"
 | 
			
		||||
	caldata_dd $source $target $count $offset
 | 
			
		||||
	if [ $? != 0 ]; then
 | 
			
		||||
		echo 1 > "$target_dir/loading"
 | 
			
		||||
		caldata_die "failed to extract calibration data from $source"
 | 
			
		||||
	else
 | 
			
		||||
		echo 0 > "$target_dir/loading"
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_valid() {
 | 
			
		||||
	local expected="$1"
 | 
			
		||||
	local target=$2
 | 
			
		||||
 | 
			
		||||
	[ -n "$target" ] || target=/lib/firmware/$FIRMWARE
 | 
			
		||||
 | 
			
		||||
	magic=$(hexdump -v -n 2 -e '1/1 "%02x"' $target)
 | 
			
		||||
	[ "$magic" = "$expected" ]
 | 
			
		||||
	return $?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_patch_chksum() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local mac_offset=$(($2))
 | 
			
		||||
	local chksum_offset=$(($3))
 | 
			
		||||
	local target=$4
 | 
			
		||||
	local xor_mac
 | 
			
		||||
	local xor_fw_mac
 | 
			
		||||
	local xor_fw_chksum
 | 
			
		||||
 | 
			
		||||
	xor_mac=${mac//:/}
 | 
			
		||||
	xor_mac="${xor_mac:0:4} ${xor_mac:4:4} ${xor_mac:8:4}"
 | 
			
		||||
 | 
			
		||||
	xor_fw_mac=$(hexdump -v -n 6 -s $mac_offset -e '/1 "%02x"' /lib/firmware/$FIRMWARE)
 | 
			
		||||
	xor_fw_mac="${xor_fw_mac:0:4} ${xor_fw_mac:4:4} ${xor_fw_mac:8:4}"
 | 
			
		||||
 | 
			
		||||
	xor_fw_chksum=$(hexdump -v -n 2 -s $chksum_offset -e '/1 "%02x"' /lib/firmware/$FIRMWARE)
 | 
			
		||||
	xor_fw_chksum=$(xor $xor_fw_chksum $xor_fw_mac $xor_mac)
 | 
			
		||||
 | 
			
		||||
	printf "%b" "\x${xor_fw_chksum:0:2}\x${xor_fw_chksum:2:2}" | \
 | 
			
		||||
		dd of=$target conv=notrunc bs=1 seek=$chksum_offset count=2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
caldata_patch_mac() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local mac_offset=$(($2))
 | 
			
		||||
	local chksum_offset=$3
 | 
			
		||||
	local target=$4
 | 
			
		||||
 | 
			
		||||
	[ -z "$mac" -o -z "$mac_offset" ] && return
 | 
			
		||||
 | 
			
		||||
	[ -n "$target" ] || target=/lib/firmware/$FIRMWARE
 | 
			
		||||
 | 
			
		||||
	[ -n "$chksum_offset" ] && caldata_patch_chksum "$mac" "$mac_offset" "$chksum_offset" "$target"
 | 
			
		||||
 | 
			
		||||
	macaddr_2bin $mac | dd of=$target conv=notrunc oflag=seek_bytes bs=6 seek=$mac_offset count=1 || \
 | 
			
		||||
		caldata_die "failed to write MAC address to eeprom file"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ath9k_patch_mac() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local target=$2
 | 
			
		||||
 | 
			
		||||
	caldata_patch_mac "$mac" 0x2 "" "$target"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ath9k_patch_mac_crc() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local mac_offset=$2
 | 
			
		||||
	local chksum_offset=$((mac_offset - 10))
 | 
			
		||||
	local target=$4
 | 
			
		||||
 | 
			
		||||
	caldata_patch_mac "$mac" "$mac_offset" "$chksum_offset" "$target"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ath10k_patch_mac() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local target=$2
 | 
			
		||||
 | 
			
		||||
	caldata_patch_mac "$mac" 0x6 0x2 "$target"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,94 +0,0 @@
 | 
			
		|||
# Copyright (C) 2013 OpenWrt.org
 | 
			
		||||
 | 
			
		||||
get_dt_led_path() {
 | 
			
		||||
	local ledpath
 | 
			
		||||
	local basepath="/proc/device-tree"
 | 
			
		||||
	local nodepath="$basepath/aliases/led-$1"
 | 
			
		||||
 | 
			
		||||
	[ -f "$nodepath" ] && ledpath=$(cat "$nodepath")
 | 
			
		||||
	[ -n "$ledpath" ] && ledpath="$basepath$ledpath"
 | 
			
		||||
 | 
			
		||||
	echo "$ledpath"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
get_dt_led() {
 | 
			
		||||
	local label
 | 
			
		||||
	local ledpath=$(get_dt_led_path $1)
 | 
			
		||||
 | 
			
		||||
	[ -n "$ledpath" ] && \
 | 
			
		||||
		label=$(cat "$ledpath/label" 2>/dev/null) || \
 | 
			
		||||
		label=$(cat "$ledpath/chan-name" 2>/dev/null) || \
 | 
			
		||||
		label=$(basename "$ledpath")
 | 
			
		||||
 | 
			
		||||
	echo "$label"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
led_set_attr() {
 | 
			
		||||
	[ -f "/sys/class/leds/$1/$2" ] && echo "$3" > "/sys/class/leds/$1/$2"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
led_timer() {
 | 
			
		||||
	led_set_attr $1 "trigger" "timer"
 | 
			
		||||
	led_set_attr $1 "delay_on" "$2"
 | 
			
		||||
	led_set_attr $1 "delay_off" "$3"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
led_on() {
 | 
			
		||||
	led_set_attr $1 "trigger" "none"
 | 
			
		||||
	led_set_attr $1 "brightness" 255
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
led_off() {
 | 
			
		||||
	led_set_attr $1 "trigger" "none"
 | 
			
		||||
	led_set_attr $1 "brightness" 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_restore_trigger() {
 | 
			
		||||
	local trigger
 | 
			
		||||
	local ledpath=$(get_dt_led_path $1)
 | 
			
		||||
 | 
			
		||||
	[ -n "$ledpath" ] && \
 | 
			
		||||
		trigger=$(cat "$ledpath/linux,default-trigger" 2>/dev/null)
 | 
			
		||||
 | 
			
		||||
	[ -n "$trigger" ] && \
 | 
			
		||||
		led_set_attr "$(get_dt_led $1)" "trigger" "$trigger"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_set_timer() {
 | 
			
		||||
	led_timer $status_led "$1" "$2"
 | 
			
		||||
	[ -n "$status_led2" ] && led_timer $status_led2 "$1" "$2"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_set_heartbeat() {
 | 
			
		||||
	led_set_attr $status_led "trigger" "heartbeat"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_on() {
 | 
			
		||||
	led_on $status_led
 | 
			
		||||
	[ -n "$status_led2" ] && led_on $status_led2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_off() {
 | 
			
		||||
	led_off $status_led
 | 
			
		||||
	[ -n "$status_led2" ] && led_off $status_led2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_blink_slow() {
 | 
			
		||||
	led_timer $status_led 1000 1000
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_blink_fast() {
 | 
			
		||||
	led_timer $status_led 100 100
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_blink_preinit() {
 | 
			
		||||
	led_timer $status_led 100 100
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_blink_failsafe() {
 | 
			
		||||
	led_timer $status_led 50 50
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
status_led_blink_preinit_regular() {
 | 
			
		||||
	led_timer $status_led 200 200
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,67 +0,0 @@
 | 
			
		|||
. /lib/functions.sh
 | 
			
		||||
 | 
			
		||||
migrate_led_sysfs() {
 | 
			
		||||
	local cfg="$1"; shift
 | 
			
		||||
	local tuples="$@"
 | 
			
		||||
	local sysfs
 | 
			
		||||
	local name
 | 
			
		||||
 | 
			
		||||
	config_get sysfs ${cfg} sysfs
 | 
			
		||||
	config_get name ${cfg} name
 | 
			
		||||
 | 
			
		||||
	[ -z "${sysfs}" ] && return
 | 
			
		||||
 | 
			
		||||
	for tuple in ${tuples}; do
 | 
			
		||||
		local old=${tuple%=*}
 | 
			
		||||
		local new=${tuple#*=}
 | 
			
		||||
		local new_sysfs
 | 
			
		||||
 | 
			
		||||
		new_sysfs=$(echo ${sysfs} | sed "s/${old}/${new}/")
 | 
			
		||||
 | 
			
		||||
		[ "${new_sysfs}" = "${sysfs}" ] && continue
 | 
			
		||||
 | 
			
		||||
		uci set system.${cfg}.sysfs="${new_sysfs}"
 | 
			
		||||
 | 
			
		||||
		logger -t led-migration "sysfs option of LED \"${name}\" updated to ${new_sysfs}"
 | 
			
		||||
	done;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
remove_devicename_led_sysfs() {
 | 
			
		||||
	local cfg="$1"; shift
 | 
			
		||||
	local exceptions="$@"
 | 
			
		||||
	local sysfs
 | 
			
		||||
	local name
 | 
			
		||||
	local new_sysfs
 | 
			
		||||
 | 
			
		||||
	config_get sysfs ${cfg} sysfs
 | 
			
		||||
	config_get name ${cfg} name
 | 
			
		||||
 | 
			
		||||
	# only continue if two or more colons are present
 | 
			
		||||
	echo "${sysfs}" | grep -q ":.*:" || return
 | 
			
		||||
 | 
			
		||||
	for exception in ${exceptions}; do
 | 
			
		||||
		# no change if exceptions provided as argument are found for devicename
 | 
			
		||||
		echo "${sysfs}" | grep -q "^${exception}:" && return
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	new_sysfs=$(echo ${sysfs} | sed "s/^[^:]*://")
 | 
			
		||||
 | 
			
		||||
	uci set system.${cfg}.sysfs="${new_sysfs}"
 | 
			
		||||
 | 
			
		||||
	logger -t led-migration "sysfs option of LED \"${name}\" updated to ${new_sysfs}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
migrate_leds() {
 | 
			
		||||
	config_load system
 | 
			
		||||
	config_foreach migrate_led_sysfs led "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
remove_devicename_leds() {
 | 
			
		||||
	config_load system
 | 
			
		||||
	config_foreach remove_devicename_led_sysfs led "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
migrations_apply() {
 | 
			
		||||
	local realm="$1"
 | 
			
		||||
	[ -n "$(uci changes ${realm})" ] && uci -q commit ${realm}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,247 +0,0 @@
 | 
			
		|||
#Mobile configuration management lib
 | 
			
		||||
 | 
			
		||||
. /usr/share/libubox/jshn.sh
 | 
			
		||||
. /lib/functions.sh
 | 
			
		||||
 | 
			
		||||
gsm_soft_reset() {
 | 
			
		||||
	gsmctl -n -A at+cfun=4
 | 
			
		||||
	sleep 2
 | 
			
		||||
	gsmctl -n -A at+cfun=1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
qmi_error_handle() {
 | 
			
		||||
	local error="$1"
 | 
			
		||||
	local modem_id="$2"
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "error") && {
 | 
			
		||||
		echo "$error"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "Client IDs exhausted") && {
 | 
			
		||||
			echo "ClientIdsExhausted! reseting counter..."
 | 
			
		||||
			proto_notify_error "$interface" NO_CID
 | 
			
		||||
			uqmi -s -d "$device" --sync
 | 
			
		||||
			return 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#	Reik papildyt ERROR handlinima
 | 
			
		||||
#	$(echo "$error" | grep -q "multiple-connection-to-same-pdn-not-allowed") && {
 | 
			
		||||
#			echo "Reseting due dublicated connection..."
 | 
			
		||||
#			qmicli -p -d "$device" --uim-sim-power-off=1
 | 
			
		||||
#			qmicli -p -d "$device" --uim-sim-power-on=1
 | 
			
		||||
#			return 1
 | 
			
		||||
#	}
 | 
			
		||||
 | 
			
		||||
#	$(echo "$error" | grep -q "Transaction timed out") && {
 | 
			
		||||
#			echo "Device not responding, restarting module"
 | 
			
		||||
#			gsmctl -O $modem_id -A at+cfun=1,1
 | 
			
		||||
#	}
 | 
			
		||||
#
 | 
			
		||||
#	$(echo "$error" | grep -q 'verbose call end reason (2,236)') && {
 | 
			
		||||
   #                     echo "Failed to start network, clearing all cids"
 | 
			
		||||
  #                      qmicli -p -d "$device" --wds-noop --device-open-sync
 | 
			
		||||
 #                       return 1
 | 
			
		||||
#        }
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "Call Failed") && {
 | 
			
		||||
		echo "Device not responding, restarting module"
 | 
			
		||||
		sleep 10
 | 
			
		||||
		gsm_soft_reset
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "Policy Mismatch") && {
 | 
			
		||||
		echo "Reseting network..."
 | 
			
		||||
		gsm_soft_reset
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "Failed to connect to service") && {
 | 
			
		||||
		echo "Device not responding, restarting module"
 | 
			
		||||
		gsmctl -A at+cfun=1,1
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	$(echo "$error" | grep -q "error") && {
 | 
			
		||||
		echo "$error"
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
passthrough_mode=
 | 
			
		||||
get_passthrough() {
 | 
			
		||||
	config_get primary "$1" primary
 | 
			
		||||
	[ "$primary" = "1" ] && {
 | 
			
		||||
		config_get sim "$1" position;
 | 
			
		||||
		passthrough_mode=$(uci -q get network.mob1s${sim}a1.passthrough_mode 2>/dev/null);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
setup_bridge_v4() {
 | 
			
		||||
	local dev="$1"
 | 
			
		||||
	local dhcp_param_file="/tmp/dnsmasq.d/bridge"
 | 
			
		||||
	echo "$parameters4"
 | 
			
		||||
 | 
			
		||||
	json_load "$parameters4"
 | 
			
		||||
	json_select "ipv4"
 | 
			
		||||
	json_get_var bridge_ipaddr ip
 | 
			
		||||
	json_get_var bridge_mask subnet
 | 
			
		||||
	json_get_var bridge_gateway gateway
 | 
			
		||||
	json_get_var bridge_dns1 dns1
 | 
			
		||||
	json_get_var bridge_dns2 dns2
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string name "${interface}_4"
 | 
			
		||||
	json_add_string ifname "$dev"
 | 
			
		||||
	json_add_string proto "none"
 | 
			
		||||
	json_add_object "data"
 | 
			
		||||
	ubus call network add_dynamic "$(json_dump)"
 | 
			
		||||
	IFACE4="${interface}_4"
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string interface "${interface}_4"
 | 
			
		||||
	json_add_string zone "lan"
 | 
			
		||||
	ubus call network.interface set_data "$(json_dump)"
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string interface "${interface}"
 | 
			
		||||
	json_add_string bridge_ipaddr "$bridge_ipaddr"
 | 
			
		||||
	ubus call network.interface set_data "$(json_dump)"
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string modem "$modem"
 | 
			
		||||
	json_add_string sim "$sim"
 | 
			
		||||
	ubus call network.interface."${interface}_4" set_data "$(json_dump)"
 | 
			
		||||
	json_close_object
 | 
			
		||||
 | 
			
		||||
	ip route add default dev "$dev" table 42
 | 
			
		||||
	ip route add default dev br-lan table 43
 | 
			
		||||
	ip route add "$bridge_ipaddr" dev br-lan
 | 
			
		||||
 | 
			
		||||
	ip rule add pref 5042 from "$bridge_ipaddr" lookup 42
 | 
			
		||||
	ip rule add pref 5043 iif "$dev" lookup 43
 | 
			
		||||
	#sysctl -w net.ipv4.conf.br-lan.proxy_arp=1 #2>/dev/null
 | 
			
		||||
	ip neighbor add proxy "$bridge_gateway" dev br-lan
 | 
			
		||||
 | 
			
		||||
        iptables -A postrouting_rule -m comment --comment "Bridge mode" -o "$dev" -j ACCEPT -tnat
 | 
			
		||||
 | 
			
		||||
	config_load simcard
 | 
			
		||||
	config_foreach get_passthrough sim
 | 
			
		||||
 | 
			
		||||
	> $dhcp_param_file
 | 
			
		||||
	[ -z "$mac" ] && mac="*:*:*:*:*:*"
 | 
			
		||||
	[ "$passthrough_mode" != "no_dhcp" ] && {
 | 
			
		||||
		echo "dhcp-range=tag:mobbridge,$bridge_ipaddr,static,$bridge_mask,${leasetime:-1h}" > "$dhcp_param_file"
 | 
			
		||||
		echo "shared-network=br-lan,$bridge_ipaddr" >> "$dhcp_param_file"
 | 
			
		||||
		echo "dhcp-host=$mac,set:mobbridge,$bridge_ipaddr" >> "$dhcp_param_file"
 | 
			
		||||
		echo "dhcp-option=tag:mobbridge,br-lan,3,$bridge_gateway" >> "$dhcp_param_file"
 | 
			
		||||
		echo "dhcp-option=tag:mobbridge,br-lan,6,$bridge_dns1,$bridge_dns2" >> "$dhcp_param_file"
 | 
			
		||||
		echo "server=$bridge_dns1" >> "$dhcp_param_file"
 | 
			
		||||
		echo "server=$bridge_dns2" >> "$dhcp_param_file"
 | 
			
		||||
	}
 | 
			
		||||
	/etc/init.d/dnsmasq reload
 | 
			
		||||
	swconfig dev 'switch0' set soft_reset 5 &
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
setup_static_v4() {
 | 
			
		||||
	local dev="$1"
 | 
			
		||||
	echo "Setting up $dev V4 static"
 | 
			
		||||
	echo "$parameters4"
 | 
			
		||||
 | 
			
		||||
	json_load "$parameters4"
 | 
			
		||||
	json_select "ipv4"
 | 
			
		||||
	json_get_var ip_4 ip
 | 
			
		||||
	json_get_var dns1_4 dns1
 | 
			
		||||
	json_get_var dns2_4 dns2
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string name "${interface}_4"
 | 
			
		||||
	json_add_string ifname "$dev"
 | 
			
		||||
	json_add_string proto static
 | 
			
		||||
	json_add_string gateway "0.0.0.0"
 | 
			
		||||
 | 
			
		||||
	json_add_array ipaddr
 | 
			
		||||
		json_add_string "" "$ip_4"
 | 
			
		||||
	json_close_array
 | 
			
		||||
 | 
			
		||||
	json_add_array dns
 | 
			
		||||
		[ -n "$dns1_4" ] && json_add_string "" "$dns1_4"
 | 
			
		||||
		[ -n "$dns2_4" ] && json_add_string "" "$dns2_4"
 | 
			
		||||
	json_close_array
 | 
			
		||||
 | 
			
		||||
	[ -n "$ip4table" ] && json_add_string ip4table "$ip4table"
 | 
			
		||||
	proto_add_dynamic_defaults
 | 
			
		||||
 | 
			
		||||
	ubus call network add_dynamic "$(json_dump)"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
setup_dhcp_v4() {
 | 
			
		||||
	local dev="$1"
 | 
			
		||||
	echo "Setting up $dev V4 DCHP"
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string name "${interface}_4"
 | 
			
		||||
	json_add_string ifname "$dev"
 | 
			
		||||
	json_add_string proto "dhcp"
 | 
			
		||||
	json_add_string script "/lib/netifd/dhcp_mobile.script"
 | 
			
		||||
	[ -n "$ip4table" ] && json_add_string ip4table "$ip4table"
 | 
			
		||||
	proto_add_dynamic_defaults
 | 
			
		||||
	[ -n "$zone" ] && json_add_string zone "$zone"
 | 
			
		||||
	ubus call network add_dynamic "$(json_dump)"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
setup_dhcp_v6() {
 | 
			
		||||
	local dev="$1"
 | 
			
		||||
	echo "Setting up $dev V6 DHCP"
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string name "${interface}_6"
 | 
			
		||||
	json_add_string ifname "$dev"
 | 
			
		||||
	json_add_string proto "dhcpv6"
 | 
			
		||||
	[ -n "$ip6table" ] && json_add_string ip6table "$ip6table"
 | 
			
		||||
	json_add_boolean ignore_valid 1
 | 
			
		||||
	proto_add_dynamic_defaults
 | 
			
		||||
	# RFC 7278: Extend an IPv6 /64 Prefix to LAN
 | 
			
		||||
	json_add_string extendprefix 1
 | 
			
		||||
	[ -n "$zone" ] && json_add_string zone "$zone"
 | 
			
		||||
	ubus call network add_dynamic "$(json_dump)"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
setup_static_v6() {
 | 
			
		||||
	local dev="$1"
 | 
			
		||||
	echo "Setting up $dev V6 static"
 | 
			
		||||
	echo "$parameters6"
 | 
			
		||||
 | 
			
		||||
	json_load "$parameters6"
 | 
			
		||||
	json_select "ipv6"
 | 
			
		||||
	json_get_var ip6_with_prefix ip
 | 
			
		||||
	ip_6="${ip6_with_prefix%/*}"
 | 
			
		||||
	ip_prefix_length="${ip6_with_prefix#*/}"
 | 
			
		||||
	json_get_var ip6_gateway_with_prefix gateway
 | 
			
		||||
	gateway_6="${ip6_gateway_with_prefix%/*}"
 | 
			
		||||
	json_get_var dns1_6 dns1
 | 
			
		||||
	json_get_var dns2_6 dns2
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_add_string name "${interface}_6"
 | 
			
		||||
	json_add_string ifname "$dev"
 | 
			
		||||
	json_add_string proto static
 | 
			
		||||
	json_add_string ip6gw "$gateway_6"
 | 
			
		||||
 | 
			
		||||
	json_add_array ip6prefix
 | 
			
		||||
	json_add_string "" "$ip6_with_prefix"
 | 
			
		||||
	json_close_array
 | 
			
		||||
 | 
			
		||||
	json_add_array ip6addr
 | 
			
		||||
		json_add_string "" "$ip6_with_prefix"
 | 
			
		||||
	json_close_array
 | 
			
		||||
 | 
			
		||||
	json_add_array dns
 | 
			
		||||
		[ -n "$dns1_6" ] && json_add_string "" "$dns1_6"
 | 
			
		||||
		[ -n "$dns2_6" ] && json_add_string "" "$dns2_6"
 | 
			
		||||
	json_close_array
 | 
			
		||||
 | 
			
		||||
	[ -n "$ip6table" ] && json_add_string ip6table "$ip6table"
 | 
			
		||||
	proto_add_dynamic_defaults
 | 
			
		||||
 | 
			
		||||
	ubus call network add_dynamic "$(json_dump)"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,87 +0,0 @@
 | 
			
		|||
# Copyright (C) 2006-2013 OpenWrt.org
 | 
			
		||||
# Copyright (C) 2010 Vertical Communications
 | 
			
		||||
 | 
			
		||||
boot_hook_splice_start() {
 | 
			
		||||
	export -n PI_HOOK_SPLICE=1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boot_hook_splice_finish() {
 | 
			
		||||
	local hook
 | 
			
		||||
	for hook in $PI_STACK_LIST; do
 | 
			
		||||
		local v; eval "v=\${${hook}_splice:+\$${hook}_splice }$hook"
 | 
			
		||||
		export -n "${hook}=${v% }"
 | 
			
		||||
		export -n "${hook}_splice="
 | 
			
		||||
	done
 | 
			
		||||
	export -n PI_HOOK_SPLICE=
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boot_hook_init() {
 | 
			
		||||
	local hook="${1}_hook"
 | 
			
		||||
	export -n "PI_STACK_LIST=${PI_STACK_LIST:+$PI_STACK_LIST }$hook"
 | 
			
		||||
	export -n "$hook="
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boot_hook_add() {
 | 
			
		||||
	local hook="${1}_hook${PI_HOOK_SPLICE:+_splice}"
 | 
			
		||||
	local func="${2}"
 | 
			
		||||
 | 
			
		||||
	[ -n "$func" ] && {
 | 
			
		||||
		local v; eval "v=\$$hook"
 | 
			
		||||
		export -n "$hook=${v:+$v }$func"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boot_hook_shift() {
 | 
			
		||||
	local hook="${1}_hook"
 | 
			
		||||
	local rvar="${2}"
 | 
			
		||||
 | 
			
		||||
	local v; eval "v=\$$hook"
 | 
			
		||||
	[ -n "$v" ] && {
 | 
			
		||||
		local first="${v%% *}"
 | 
			
		||||
 | 
			
		||||
		[ "$v" != "${v#* }" ] && \
 | 
			
		||||
			export -n "$hook=${v#* }" || \
 | 
			
		||||
			export -n "$hook="
 | 
			
		||||
 | 
			
		||||
		export -n "$rvar=$first"
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
boot_run_hook() {
 | 
			
		||||
	local hook="$1"
 | 
			
		||||
	local func
 | 
			
		||||
 | 
			
		||||
	while boot_hook_shift "$hook" func; do
 | 
			
		||||
		local ran; eval "ran=\$PI_RAN_$func"
 | 
			
		||||
		[ -n "$ran" ] || {
 | 
			
		||||
			export -n "PI_RAN_$func=1"
 | 
			
		||||
			$func "$1" "$2"
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pivot() { # <new_root> <old_root>
 | 
			
		||||
	/bin/mount -o noatime,move /proc $1/proc && \
 | 
			
		||||
	pivot_root $1 $1$2 && {
 | 
			
		||||
		/bin/mount -o noatime,move $2/dev /dev
 | 
			
		||||
		/bin/mount -o noatime,move $2/tmp /tmp
 | 
			
		||||
		/bin/mount -o noatime,move $2/sys /sys 2>&-
 | 
			
		||||
		/bin/mount -o noatime,move $2/overlay /overlay 2>&-
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fopivot() { # <rw_root> <work_dir> <ro_root> <dupe?>
 | 
			
		||||
	/bin/mount -o noatime,lowerdir=/,upperdir=$1,workdir=$2 -t overlay "overlayfs:$1" /mnt
 | 
			
		||||
	pivot /mnt $3
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ramoverlay() {
 | 
			
		||||
	mkdir -p /tmp/root
 | 
			
		||||
	/bin/mount -t tmpfs -o noatime,mode=0755 root /tmp/root
 | 
			
		||||
	mkdir -p /tmp/root/root /tmp/root/work
 | 
			
		||||
	fopivot /tmp/root/root /tmp/root/work /rom 1
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,103 +0,0 @@
 | 
			
		|||
#
 | 
			
		||||
# service: simple wrapper around start-stop-daemon
 | 
			
		||||
#
 | 
			
		||||
# Usage: service ACTION EXEC ARGS...
 | 
			
		||||
#
 | 
			
		||||
# Action:
 | 
			
		||||
#   -C	check if EXEC is alive
 | 
			
		||||
#   -S	start EXEC, passing it ARGS as its arguments
 | 
			
		||||
#   -K	kill EXEC, sending it a TERM signal if not specified otherwise
 | 
			
		||||
#
 | 
			
		||||
# Environment variables exposed:
 | 
			
		||||
#   SERVICE_DAEMONIZE	run EXEC in background
 | 
			
		||||
#   SERVICE_WRITE_PID	create a pid-file and use it for matching
 | 
			
		||||
#   SERVICE_MATCH_EXEC	use EXEC command-line for matching (default)
 | 
			
		||||
#   SERVICE_MATCH_NAME	use EXEC process name for matching
 | 
			
		||||
#   SERVICE_USE_PID	assume EXEC create its own pid-file and use it for matching
 | 
			
		||||
#   SERVICE_NAME	process name to use (default to EXEC file part)
 | 
			
		||||
#   SERVICE_PID_FILE	pid file to use (default to /var/run/$SERVICE_NAME.pid)
 | 
			
		||||
#   SERVICE_SIG		signal to send when using -K
 | 
			
		||||
#   SERVICE_SIG_RELOAD	default signal used when reloading
 | 
			
		||||
#   SERVICE_SIG_STOP	default signal used when stopping
 | 
			
		||||
#   SERVICE_STOP_TIME	time to wait for a process to stop gracefully before killing it
 | 
			
		||||
#   SERVICE_UID		user EXEC should be run as
 | 
			
		||||
#   SERVICE_GID		group EXEC should be run as
 | 
			
		||||
#
 | 
			
		||||
#   SERVICE_DEBUG	don't do anything, but show what would be done
 | 
			
		||||
#   SERVICE_QUIET	don't print anything
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
SERVICE_QUIET=1
 | 
			
		||||
SERVICE_SIG_RELOAD="HUP"
 | 
			
		||||
SERVICE_SIG_STOP="TERM"
 | 
			
		||||
SERVICE_STOP_TIME=5
 | 
			
		||||
SERVICE_MATCH_EXEC=1
 | 
			
		||||
 | 
			
		||||
service() {
 | 
			
		||||
	local ssd
 | 
			
		||||
	local exec
 | 
			
		||||
	local name
 | 
			
		||||
	local start
 | 
			
		||||
	ssd="${SERVICE_DEBUG:+echo }start-stop-daemon${SERVICE_QUIET:+ -q}"
 | 
			
		||||
	case "$1" in
 | 
			
		||||
	  -C)
 | 
			
		||||
		ssd="$ssd -K -t"
 | 
			
		||||
		;;
 | 
			
		||||
	  -S)
 | 
			
		||||
		ssd="$ssd -S${SERVICE_DAEMONIZE:+ -b}${SERVICE_WRITE_PID:+ -m}"
 | 
			
		||||
		start=1
 | 
			
		||||
		;;
 | 
			
		||||
	  -K)
 | 
			
		||||
		ssd="$ssd -K${SERVICE_SIG:+ -s $SERVICE_SIG}"
 | 
			
		||||
		;;
 | 
			
		||||
	  *)
 | 
			
		||||
		echo "service: unknown ACTION '$1'" 1>&2
 | 
			
		||||
		return 1
 | 
			
		||||
	esac
 | 
			
		||||
	shift
 | 
			
		||||
	exec="$1"
 | 
			
		||||
	[ -n "$exec" ] || {
 | 
			
		||||
		echo "service: missing argument" 1>&2
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	[ -x "$exec" ] || {
 | 
			
		||||
		echo "service: file '$exec' is not executable" 1>&2
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	name="${SERVICE_NAME:-${exec##*/}}"
 | 
			
		||||
	[ -z "$SERVICE_USE_PID$SERVICE_WRITE_PID$SERVICE_PID_FILE" ] \
 | 
			
		||||
		|| ssd="$ssd -p ${SERVICE_PID_FILE:-/var/run/$name.pid}"
 | 
			
		||||
	[ -z "$SERVICE_MATCH_NAME" ] || ssd="$ssd -n $name"
 | 
			
		||||
	ssd="$ssd${SERVICE_UID:+ -c $SERVICE_UID${SERVICE_GID:+:$SERVICE_GID}}"
 | 
			
		||||
	[ -z "$SERVICE_MATCH_EXEC$start" ] || ssd="$ssd -x $exec"
 | 
			
		||||
	shift
 | 
			
		||||
	$ssd${1:+ -- "$@"}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
service_check() {
 | 
			
		||||
	service -C "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
service_signal() {
 | 
			
		||||
	SERVICE_SIG="${SERVICE_SIG:-USR1}" service -K "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
service_start() {
 | 
			
		||||
	service -S "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
service_stop() {
 | 
			
		||||
	local try
 | 
			
		||||
	SERVICE_SIG="${SERVICE_SIG:-$SERVICE_SIG_STOP}" service -K "$@" || return 1
 | 
			
		||||
	while [ $((try++)) -lt $SERVICE_STOP_TIME ]; do
 | 
			
		||||
		service -C "$@" || return 0
 | 
			
		||||
		sleep 1
 | 
			
		||||
	done
 | 
			
		||||
	SERVICE_SIG="KILL" service -K "$@"
 | 
			
		||||
	sleep 1
 | 
			
		||||
	! service -C "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
service_reload() {
 | 
			
		||||
	SERVICE_SIG="${SERVICE_SIG:-$SERVICE_SIG_RELOAD}" service -K "$@"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,226 +0,0 @@
 | 
			
		|||
# Copyright (C) 2006-2013 OpenWrt.org
 | 
			
		||||
 | 
			
		||||
. /lib/functions.sh
 | 
			
		||||
. /usr/share/libubox/jshn.sh
 | 
			
		||||
 | 
			
		||||
get_mac_binary() {
 | 
			
		||||
	local path="$1"
 | 
			
		||||
	local offset="$2"
 | 
			
		||||
 | 
			
		||||
	if ! [ -e "$path" ]; then
 | 
			
		||||
		echo "get_mac_binary: file $path not found!" >&2
 | 
			
		||||
		return
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	hexdump -v -n 6 -s $offset -e '5/1 "%02x:" 1/1 "%02x"' $path 2>/dev/null
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
get_mac_label_dt() {
 | 
			
		||||
	local basepath="/proc/device-tree"
 | 
			
		||||
	local macdevice="$(cat "$basepath/aliases/label-mac-device" 2>/dev/null)"
 | 
			
		||||
	local macaddr
 | 
			
		||||
 | 
			
		||||
	[ -n "$macdevice" ] || return
 | 
			
		||||
 | 
			
		||||
	macaddr=$(get_mac_binary "$basepath/$macdevice/mac-address" 0 2>/dev/null)
 | 
			
		||||
	[ -n "$macaddr" ] || macaddr=$(get_mac_binary "$basepath/$macdevice/local-mac-address" 0 2>/dev/null)
 | 
			
		||||
 | 
			
		||||
	echo $macaddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
get_mac_label_json() {
 | 
			
		||||
	local cfg="/etc/board.json"
 | 
			
		||||
	local macaddr
 | 
			
		||||
 | 
			
		||||
	[ -s "$cfg" ] || return
 | 
			
		||||
 | 
			
		||||
	json_init
 | 
			
		||||
	json_load "$(cat $cfg)"
 | 
			
		||||
	if json_is_a system object; then
 | 
			
		||||
		json_select system
 | 
			
		||||
			json_get_var macaddr label_macaddr
 | 
			
		||||
		json_select ..
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	echo $macaddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
get_mac_label() {
 | 
			
		||||
	local macaddr=$(get_mac_label_dt)
 | 
			
		||||
 | 
			
		||||
	[ -n "$macaddr" ] || macaddr=$(get_mac_label_json)
 | 
			
		||||
 | 
			
		||||
	echo $macaddr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
find_mtd_chardev() {
 | 
			
		||||
	local INDEX=$(find_mtd_index "$1")
 | 
			
		||||
	local PREFIX=/dev/mtd
 | 
			
		||||
 | 
			
		||||
	[ -d /dev/mtd ] && PREFIX=/dev/mtd/
 | 
			
		||||
	echo "${INDEX:+$PREFIX$INDEX}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mtd_get_mac_ascii() {
 | 
			
		||||
	local mtdname="$1"
 | 
			
		||||
	local key="$2"
 | 
			
		||||
	local part
 | 
			
		||||
	local mac_dirty
 | 
			
		||||
 | 
			
		||||
	part=$(find_mtd_part "$mtdname")
 | 
			
		||||
	if [ -z "$part" ]; then
 | 
			
		||||
		echo "mtd_get_mac_ascii: partition $mtdname not found!" >&2
 | 
			
		||||
		return
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	mac_dirty=$(strings "$part" | sed -n 's/^'"$key"'=//p')
 | 
			
		||||
 | 
			
		||||
	# "canonicalize" mac
 | 
			
		||||
	[ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mtd_get_mac_text() {
 | 
			
		||||
	local mtdname=$1
 | 
			
		||||
	local offset=$(($2))
 | 
			
		||||
	local part
 | 
			
		||||
	local mac_dirty
 | 
			
		||||
 | 
			
		||||
	part=$(find_mtd_part "$mtdname")
 | 
			
		||||
	if [ -z "$part" ]; then
 | 
			
		||||
		echo "mtd_get_mac_text: partition $mtdname not found!" >&2
 | 
			
		||||
		return
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	if [ -z "$offset" ]; then
 | 
			
		||||
		echo "mtd_get_mac_text: offset missing!" >&2
 | 
			
		||||
		return
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	mac_dirty=$(dd if="$part" bs=1 skip="$offset" count=17 2>/dev/null)
 | 
			
		||||
 | 
			
		||||
	# "canonicalize" mac
 | 
			
		||||
	[ -n "$mac_dirty" ] && macaddr_canonicalize "$mac_dirty"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mtd_get_mac_binary() {
 | 
			
		||||
	local mtdname="$1"
 | 
			
		||||
	local offset="$2"
 | 
			
		||||
	local part
 | 
			
		||||
 | 
			
		||||
	part=$(find_mtd_part "$mtdname")
 | 
			
		||||
	get_mac_binary "$part" "$offset"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mtd_get_mac_binary_ubi() {
 | 
			
		||||
	local mtdname="$1"
 | 
			
		||||
	local offset="$2"
 | 
			
		||||
 | 
			
		||||
	. /lib/upgrade/nand.sh
 | 
			
		||||
 | 
			
		||||
	local ubidev=$(nand_find_ubi $CI_UBIPART)
 | 
			
		||||
	local part=$(nand_find_volume $ubidev $1)
 | 
			
		||||
 | 
			
		||||
	get_mac_binary "/dev/$part" "$offset"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mtd_get_part_size() {
 | 
			
		||||
	local part_name=$1
 | 
			
		||||
	local first dev size erasesize name
 | 
			
		||||
	while read dev size erasesize name; do
 | 
			
		||||
		name=${name#'"'}; name=${name%'"'}
 | 
			
		||||
		if [ "$name" = "$part_name" ]; then
 | 
			
		||||
			echo $((0x$size))
 | 
			
		||||
			break
 | 
			
		||||
		fi
 | 
			
		||||
	done < /proc/mtd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_add() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local val=$2
 | 
			
		||||
	local oui=${mac%:*:*:*}
 | 
			
		||||
	local nic=${mac#*:*:*:}
 | 
			
		||||
 | 
			
		||||
	nic=$(printf "%06x" $((0x${nic//:/} + val & 0xffffff)) | sed 's/^\(.\{2\}\)\(.\{2\}\)\(.\{2\}\)/\1:\2:\3/')
 | 
			
		||||
	echo $oui:$nic
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_geteui() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local sep=$2
 | 
			
		||||
 | 
			
		||||
	echo ${mac:9:2}$sep${mac:12:2}$sep${mac:15:2}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_setbit() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local bit=${2:-0}
 | 
			
		||||
 | 
			
		||||
	[ $bit -gt 0 -a $bit -le 48 ] || return
 | 
			
		||||
 | 
			
		||||
	printf "%012x" $(( 0x${mac//:/} | 2**(48-bit) )) | sed -e 's/\(.\{2\}\)/\1:/g' -e 's/:$//'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_unsetbit() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
	local bit=${2:-0}
 | 
			
		||||
 | 
			
		||||
	[ $bit -gt 0 -a $bit -le 48 ] || return
 | 
			
		||||
 | 
			
		||||
	printf "%012x" $(( 0x${mac//:/} & ~(2**(48-bit)) )) | sed -e 's/\(.\{2\}\)/\1:/g' -e 's/:$//'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_setbit_la() {
 | 
			
		||||
	macaddr_setbit $1 7
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_unsetbit_mc() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
 | 
			
		||||
	printf "%02x:%s" $((0x${mac%%:*} & ~0x01)) ${mac#*:}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_random() {
 | 
			
		||||
	local randsrc=$(get_mac_binary /dev/urandom 0)
 | 
			
		||||
	
 | 
			
		||||
	echo "$(macaddr_unsetbit_mc "$(macaddr_setbit_la "${randsrc}")")"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_2bin() {
 | 
			
		||||
	local mac=$1
 | 
			
		||||
 | 
			
		||||
	echo -ne \\x${mac//:/\\x}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
macaddr_canonicalize() {
 | 
			
		||||
	local mac="$1"
 | 
			
		||||
	local canon=""
 | 
			
		||||
 | 
			
		||||
	mac=$(echo -n $mac | tr -d \")
 | 
			
		||||
	[ ${#mac} -gt 17 ] && return
 | 
			
		||||
	[ -n "${mac//[a-fA-F0-9\.: -]/}" ] && return
 | 
			
		||||
 | 
			
		||||
	for octet in ${mac//[\.:-]/ }; do
 | 
			
		||||
		case "${#octet}" in
 | 
			
		||||
		1)
 | 
			
		||||
			octet="0${octet}"
 | 
			
		||||
			;;
 | 
			
		||||
		2)
 | 
			
		||||
			;;
 | 
			
		||||
		4)
 | 
			
		||||
			octet="${octet:0:2} ${octet:2:2}"
 | 
			
		||||
			;;
 | 
			
		||||
		12)
 | 
			
		||||
			octet="${octet:0:2} ${octet:2:2} ${octet:4:2} ${octet:6:2} ${octet:8:2} ${octet:10:2}"
 | 
			
		||||
			;;
 | 
			
		||||
		*)
 | 
			
		||||
			return
 | 
			
		||||
			;;
 | 
			
		||||
		esac
 | 
			
		||||
		canon=${canon}${canon:+ }${octet}
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	[ ${#canon} -ne 17 ] && return
 | 
			
		||||
 | 
			
		||||
	printf "%02x:%02x:%02x:%02x:%02x:%02x" 0x${canon// / 0x} 2>/dev/null
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,383 +0,0 @@
 | 
			
		|||
find_mmc_part() {
 | 
			
		||||
    local DEVNAME PARTNAME
 | 
			
		||||
 | 
			
		||||
    if grep -q "$1" /proc/mtd; then
 | 
			
		||||
        echo "" && return 0
 | 
			
		||||
    fi
 | 
			
		||||
 | 
			
		||||
    for DEVNAME in /sys/block/mmcblk0/mmcblk*p*; do
 | 
			
		||||
        PARTNAME=$(grep PARTNAME ${DEVNAME}/uevent | cut -f2 -d'=')
 | 
			
		||||
        [ "$PARTNAME" = "$1" ] && echo "/dev/$(basename $DEVNAME)" && return 0
 | 
			
		||||
    done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
get_full_section_name() {
 | 
			
		||||
	local img=$1
 | 
			
		||||
	local sec=$2
 | 
			
		||||
 | 
			
		||||
	dumpimage -l ${img} | grep "^ Image.*(${sec})" | \
 | 
			
		||||
		sed 's,^ Image.*(\(.*\)),\1,'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_contains() {
 | 
			
		||||
	local img=$1
 | 
			
		||||
	local sec=$2
 | 
			
		||||
	dumpimage -l ${img} | grep -q "^ Image.*(${sec}.*)" || return 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
print_sections() {
 | 
			
		||||
	local img=$1
 | 
			
		||||
 | 
			
		||||
	dumpimage -l ${img} | awk '/^ Image.*(.*)/ { print gensub(/Image .* \((.*)\)/,"\\1", $0) }'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_has_mandatory_section() {
 | 
			
		||||
	local img=$1
 | 
			
		||||
	local mandatory_sections=$2
 | 
			
		||||
 | 
			
		||||
	for sec in ${mandatory_sections}; do
 | 
			
		||||
		image_contains $img ${sec} || {\
 | 
			
		||||
			return 1
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_demux() {
 | 
			
		||||
	local img=$1
 | 
			
		||||
 | 
			
		||||
	for sec in $(print_sections ${img}); do
 | 
			
		||||
		local fullname=$(get_full_section_name ${img} ${sec})
 | 
			
		||||
 | 
			
		||||
		local position=$(dumpimage -l ${img} | grep "(${fullname})" | awk '{print $2}')
 | 
			
		||||
		dumpimage -i ${img} -o /tmp/${fullname}.bin -T "flat_dt" -p "${position}" ${fullname} > /dev/null || { \
 | 
			
		||||
			echo "Error while extracting \"${sec}\" from ${img}"
 | 
			
		||||
			return 1
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
image_is_FIT() {
 | 
			
		||||
	if ! dumpimage -l $1 > /dev/null 2>&1; then
 | 
			
		||||
		echo "$1 is not a valid FIT image"
 | 
			
		||||
		return 1
 | 
			
		||||
	fi
 | 
			
		||||
	return 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
switch_layout() {
 | 
			
		||||
	local layout=$1
 | 
			
		||||
	local boot_layout=`find / -name boot_layout`
 | 
			
		||||
 | 
			
		||||
	# Layout switching is only required as the  boot images (up to u-boot)
 | 
			
		||||
	# use 512 user data bytes per code word, whereas Linux uses 516 bytes.
 | 
			
		||||
	# It's only applicable for NAND flash. So let's return if we don't have
 | 
			
		||||
	# one.
 | 
			
		||||
 | 
			
		||||
	[ -n "$boot_layout" ] || return
 | 
			
		||||
 | 
			
		||||
	case "${layout}" in
 | 
			
		||||
		boot|1) echo 1 > $boot_layout;;
 | 
			
		||||
		linux|0) echo 0 > $boot_layout;;
 | 
			
		||||
		*) echo "Unknown layout \"${layout}\"";;
 | 
			
		||||
	esac
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_mtd() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local mtdname=$2
 | 
			
		||||
	local append=""
 | 
			
		||||
 | 
			
		||||
	local mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
	local pgsz=$(cat /sys/class/mtd/${mtdpart}/writesize)
 | 
			
		||||
	[ -f "$CONF_TAR" -a "$SAVE_CONFIG" -eq 1 -a "$2" == "rootfs" ] && append="-j $CONF_TAR"
 | 
			
		||||
 | 
			
		||||
	dd if=/tmp/${bin}.bin bs=${pgsz} conv=sync | mtd $append -e "/dev/${mtdpart}" write - "/dev/${mtdpart}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_emmc() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local emmcblock=$2
 | 
			
		||||
 | 
			
		||||
	dd if=/dev/zero of=${emmcblock}
 | 
			
		||||
	dd if=/tmp/${bin}.bin of=${emmcblock}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_partition() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local mtdname=$2
 | 
			
		||||
	local emmcblock="$(find_mmc_part "$mtdname")"
 | 
			
		||||
 | 
			
		||||
	if [ -e "$emmcblock" ]; then
 | 
			
		||||
		do_flash_emmc $bin $emmcblock
 | 
			
		||||
	else
 | 
			
		||||
		do_flash_mtd $bin $mtdname
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_bootconfig() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local mtdname=$2
 | 
			
		||||
 | 
			
		||||
	# Fail safe upgrade
 | 
			
		||||
	if [ -f /proc/boot_info/getbinary_${bin} ]; then
 | 
			
		||||
		cat /proc/boot_info/getbinary_${bin} > /tmp/${bin}.bin
 | 
			
		||||
		do_flash_partition $bin $mtdname
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_failsafe_partition() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local mtdname=$2
 | 
			
		||||
	local emmcblock
 | 
			
		||||
	local primaryboot
 | 
			
		||||
 | 
			
		||||
	# Fail safe upgrade
 | 
			
		||||
	[ -f /proc/boot_info/$mtdname/upgradepartition ] && {
 | 
			
		||||
		default_mtd=$mtdname
 | 
			
		||||
		mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition)
 | 
			
		||||
		primaryboot=$(cat /proc/boot_info/$default_mtd/primaryboot)
 | 
			
		||||
		if [ $primaryboot -eq 0 ]; then
 | 
			
		||||
			echo 1 > /proc/boot_info/$default_mtd/primaryboot
 | 
			
		||||
		else
 | 
			
		||||
			echo 0 > /proc/boot_info/$default_mtd/primaryboot
 | 
			
		||||
		fi
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	emmcblock="$(find_mmc_part "$mtdname")"
 | 
			
		||||
 | 
			
		||||
	if [ -e "$emmcblock" ]; then
 | 
			
		||||
		do_flash_emmc $bin $emmcblock
 | 
			
		||||
	else
 | 
			
		||||
		do_flash_mtd $bin $mtdname
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_ubi() {
 | 
			
		||||
	local bin=$1
 | 
			
		||||
	local mtdname=$2
 | 
			
		||||
	local mtdpart
 | 
			
		||||
	local primaryboot
 | 
			
		||||
 | 
			
		||||
	mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
	ubidetach -p /dev/${mtdpart}
 | 
			
		||||
 | 
			
		||||
	# Fail safe upgrade
 | 
			
		||||
	[ -f /proc/boot_info/$mtdname/upgradepartition ] && {
 | 
			
		||||
		primaryboot=$(cat /proc/boot_info/$mtdname/primaryboot)
 | 
			
		||||
		if [ $primaryboot -eq 0 ]; then
 | 
			
		||||
			echo 1 > /proc/boot_info/$mtdname/primaryboot
 | 
			
		||||
		else
 | 
			
		||||
			echo 0 > /proc/boot_info/$mtdname/primaryboot
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
	ubiformat /dev/${mtdpart} -y -f /tmp/${bin}.bin
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_tz() {
 | 
			
		||||
	local sec=$1
 | 
			
		||||
	local mtdpart=$(grep "\"0:QSEE\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
	local emmcblock="$(find_mmc_part "0:QSEE")"
 | 
			
		||||
 | 
			
		||||
	if [ -n "$mtdpart" -o -e "$emmcblock" ]; then
 | 
			
		||||
		do_flash_failsafe_partition ${sec} "0:QSEE"
 | 
			
		||||
	else
 | 
			
		||||
		do_flash_failsafe_partition ${sec} "0:TZ"
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
do_flash_ddr() {
 | 
			
		||||
	local sec=$1
 | 
			
		||||
	local mtdpart=$(grep "\"0:CDT\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
	local emmcblock="$(find_mmc_part "0:CDT")"
 | 
			
		||||
 | 
			
		||||
	if [ -n "$mtdpart" -o -e "$emmcblock" ]; then
 | 
			
		||||
		do_flash_failsafe_partition ${sec} "0:CDT"
 | 
			
		||||
	else
 | 
			
		||||
		do_flash_failsafe_partition ${sec} "0:DDRPARAMS"
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
to_upper () {
 | 
			
		||||
	echo $1 | awk '{print toupper($0)}'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
flash_section() {
 | 
			
		||||
	local sec=$1
 | 
			
		||||
 | 
			
		||||
	local board=$(board_name)
 | 
			
		||||
	case "${sec}" in
 | 
			
		||||
		hlos*) switch_layout linux; do_flash_failsafe_partition ${sec} "0:HLOS";;
 | 
			
		||||
		rootfs*) switch_layout linux; do_flash_failsafe_partition ${sec} "rootfs";;
 | 
			
		||||
		fs*) switch_layout linux; do_flash_failsafe_partition ${sec} "rootfs";;
 | 
			
		||||
		ubi*) switch_layout linux; do_flash_ubi ${sec} "rootfs";;
 | 
			
		||||
		#sbl1*) switch_layout boot; do_flash_partition ${sec} "0:SBL1";;
 | 
			
		||||
		#sbl2*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:SBL2";;
 | 
			
		||||
		#sbl3*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:SBL3";;
 | 
			
		||||
		#mibib*) switch_layout boot; do_flash_partition ${sec} "0:MIBIB";;
 | 
			
		||||
		#dtb-$(to_upper $board)*) switch_layout boot; do_flash_partition ${sec} "0:DTB";;
 | 
			
		||||
		u-boot*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:APPSBL";;
 | 
			
		||||
		#ddr-$(to_upper $board)*) switch_layout boot; do_flash_ddr ${sec};;
 | 
			
		||||
		ddr-${board}-*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:CDT";;
 | 
			
		||||
		#ssd*) switch_layout boot; do_flash_partition ${sec} "0:SSD";;
 | 
			
		||||
		tz*) switch_layout boot; do_flash_tz ${sec};;
 | 
			
		||||
		#rpm*) switch_layout boot; do_flash_failsafe_partition ${sec} "0:RPM";;
 | 
			
		||||
		*) echo "Section ${sec} ignored"; return 1;;
 | 
			
		||||
	esac
 | 
			
		||||
 | 
			
		||||
	echo "Flashed ${sec}"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
erase_emmc_config() {
 | 
			
		||||
	local emmcblock="$(find_mmc_part "rootfs_data")"
 | 
			
		||||
	if [ -e "$emmcblock" ]; then
 | 
			
		||||
		dd if=/dev/zero of=${emmcblock}
 | 
			
		||||
		mkfs.ext4 "$emmcblock"
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
platform_pre_upgrade() {
 | 
			
		||||
	cp /sbin/upgraded /tmp
 | 
			
		||||
	ubus call system nandupgrade "{\"path\": \"$1\" }"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
platform_check_image_ipq() {
 | 
			
		||||
	local board=$(board_name)
 | 
			
		||||
 | 
			
		||||
	local mandatory_nand="ubi"
 | 
			
		||||
	local mandatory_nor_emmc="hlos fs"
 | 
			
		||||
	local mandatory_nor="hlos"
 | 
			
		||||
	local mandatory_section_found=0
 | 
			
		||||
	local optional="sbl2 u-boot ddr-${board} ssd tz rpm"
 | 
			
		||||
	local ignored="mibib bootconfig sbl1"
 | 
			
		||||
 | 
			
		||||
	image_is_FIT $1 || return 1
 | 
			
		||||
 | 
			
		||||
	image_has_mandatory_section $1 ${mandatory_nand} && {\
 | 
			
		||||
		mandatory_section_found=1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	image_has_mandatory_section $1 ${mandatory_nor_emmc} && {\
 | 
			
		||||
		mandatory_section_found=1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	image_has_mandatory_section $1 ${mandatory_nor} && {\
 | 
			
		||||
		mandatory_section_found=1
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if [ $mandatory_section_found -eq 0 ]; then
 | 
			
		||||
		echo "Error: mandatory section(s) missing from \"$1\". Abort..."
 | 
			
		||||
		return 1
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	for sec in ${optional}; do
 | 
			
		||||
		image_contains $1 ${sec} || {\
 | 
			
		||||
			echo "Warning: optional section \"${sec}\" missing from \"$1\". Continue..."
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	for sec in ${ignored}; do
 | 
			
		||||
		image_contains $1 ${sec} && {\
 | 
			
		||||
			echo "Warning: section \"${sec}\" will be ignored from \"$1\". Continue..."
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	image_demux $1 || {\
 | 
			
		||||
		echo "Error: \"$1\" couldn't be extracted. Abort..."
 | 
			
		||||
		return 1
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
	[ -f /tmp/hlos_version ] && rm -f /tmp/*_version
 | 
			
		||||
	dumpimage -c $1 2>/dev/null
 | 
			
		||||
	return $?
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
platform_version_upgrade() {
 | 
			
		||||
	local version_files="appsbl_version sbl_version tz_version hlos_version rpm_version"
 | 
			
		||||
	local sys="/sys/devices/system/qfprom/qfprom0/"
 | 
			
		||||
	local tmp="/tmp/"
 | 
			
		||||
 | 
			
		||||
	for file in $version_files; do
 | 
			
		||||
		[ -f "${tmp}${file}" ] && {
 | 
			
		||||
			echo "Updating "${sys}${file}" with `cat "${tmp}${file}"`"
 | 
			
		||||
			echo `cat "${tmp}${file}"` > "${sys}${file}"
 | 
			
		||||
			rm -f "${tmp}${file}"
 | 
			
		||||
		}
 | 
			
		||||
	done
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# The U-Boot loader of the OpenMesh devices requires image sizes and
 | 
			
		||||
# checksums to be provided in the U-Boot environment.
 | 
			
		||||
# The OpenMesh devices come with 2 main partitions - while one is active
 | 
			
		||||
# sysupgrade will flash the other. The boot order is changed to boot the
 | 
			
		||||
# newly flashed partition. If the new partition can't be booted due to
 | 
			
		||||
# upgrade failures the previously used partition is loaded.
 | 
			
		||||
 | 
			
		||||
platform_do_upgrade_ipq() {
 | 
			
		||||
	local board=$(board_name)
 | 
			
		||||
 | 
			
		||||
	# verify some things exist before erasing
 | 
			
		||||
	if [ ! -e $1 ]; then
 | 
			
		||||
		echo "Error: Can't find $1 after switching to ramfs, aborting upgrade!"
 | 
			
		||||
		reboot
 | 
			
		||||
	fi
 | 
			
		||||
 | 
			
		||||
	for sec in $(print_sections $1); do
 | 
			
		||||
		if [ ! -e /tmp/${sec}.bin ]; then
 | 
			
		||||
			echo "Error: Cant' find ${sec} after switching to ramfs, aborting upgrade!"
 | 
			
		||||
			reboot
 | 
			
		||||
		fi
 | 
			
		||||
	done
 | 
			
		||||
 | 
			
		||||
	case "$board" in
 | 
			
		||||
	teltonika,rutx)
 | 
			
		||||
		for sec in $(print_sections $1); do
 | 
			
		||||
			flash_section ${sec}
 | 
			
		||||
		done
 | 
			
		||||
 | 
			
		||||
		switch_layout linux
 | 
			
		||||
		# update bootconfig to register that fw upgrade has been done
 | 
			
		||||
		do_flash_bootconfig bootconfig "0:BOOTCONFIG"
 | 
			
		||||
		do_flash_bootconfig bootconfig1 "0:BOOTCONFIG1"
 | 
			
		||||
		platform_version_upgrade
 | 
			
		||||
 | 
			
		||||
		erase_emmc_config
 | 
			
		||||
		return 0;
 | 
			
		||||
		;;
 | 
			
		||||
	esac
 | 
			
		||||
 | 
			
		||||
	echo "Upgrade failed!"
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
platform_copy_config() {
 | 
			
		||||
	local emmcblock="$(find_mmc_part "rootfs_data")"
 | 
			
		||||
	mkdir -p /tmp/overlay
 | 
			
		||||
 | 
			
		||||
	if [ -e "$emmcblock" ]; then
 | 
			
		||||
		mount -t ext4 "$emmcblock" /tmp/overlay
 | 
			
		||||
		cp /tmp/sysupgrade.tgz /tmp/overlay/
 | 
			
		||||
		sync
 | 
			
		||||
		umount /tmp/overlay
 | 
			
		||||
	else
 | 
			
		||||
		local mtdname=rootfs
 | 
			
		||||
		local mtdpart
 | 
			
		||||
 | 
			
		||||
		[ -f /proc/boot_info/$mtdname/upgradepartition ] && {
 | 
			
		||||
			mtdname=$(cat /proc/boot_info/$mtdname/upgradepartition)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mtdpart=$(grep "\"${mtdname}\"" /proc/mtd | awk -F: '{print $1}')
 | 
			
		||||
		ubiattach -p /dev/${mtdpart}
 | 
			
		||||
		mount -t ubifs ubi0:rootfs_data /tmp/overlay
 | 
			
		||||
		cp /tmp/sysupgrade.tgz /tmp/overlay/
 | 
			
		||||
		sync
 | 
			
		||||
		umount /tmp/overlay
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +185,8 @@ platform_do_upgrade() {
 | 
			
		|||
		platform_do_upgrade_dualboot_datachk "$1"
 | 
			
		||||
		;;
 | 
			
		||||
	teltonika,rutx)
 | 
			
		||||
		platform_do_upgrade_ipq "$1"
 | 
			
		||||
		CI_UBIPART="rootfs"
 | 
			
		||||
		nand_do_upgrade "$1"
 | 
			
		||||
		;;
 | 
			
		||||
	zte,mf286d)
 | 
			
		||||
		CI_UBIPART="rootfs"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,165 +0,0 @@
 | 
			
		|||
From 4267880319bc1a2270d352e0ded6d6386242a7ef Mon Sep 17 00:00:00 2001
 | 
			
		||||
From: John Crispin <blogic@openwrt.org>
 | 
			
		||||
Date: Tue, 12 Aug 2014 20:49:27 +0200
 | 
			
		||||
Subject: [PATCH 24/53] GPIO: add named gpio exports
 | 
			
		||||
 | 
			
		||||
Signed-off-by: John Crispin <blogic@openwrt.org>
 | 
			
		||||
---
 | 
			
		||||
 drivers/gpio/gpiolib-of.c     |   68 +++++++++++++++++++++++++++++++++++++++++
 | 
			
		||||
 drivers/gpio/gpiolib-sysfs.c  |   10 +++++-
 | 
			
		||||
 include/asm-generic/gpio.h    |    6 ++++
 | 
			
		||||
 include/linux/gpio/consumer.h |    8 +++++
 | 
			
		||||
 4 files changed, 91 insertions(+), 1 deletion(-)
 | 
			
		||||
 | 
			
		||||
--- a/drivers/gpio/gpiolib-of.c
 | 
			
		||||
+++ b/drivers/gpio/gpiolib-of.c
 | 
			
		||||
@@ -19,6 +19,8 @@
 | 
			
		||||
 #include <linux/pinctrl/pinctrl.h>
 | 
			
		||||
 #include <linux/slab.h>
 | 
			
		||||
 #include <linux/gpio/machine.h>
 | 
			
		||||
+#include <linux/init.h>
 | 
			
		||||
+#include <linux/platform_device.h>
 | 
			
		||||
 
 | 
			
		||||
 #include "gpiolib.h"
 | 
			
		||||
 #include "gpiolib-of.h"
 | 
			
		||||
@@ -915,3 +917,68 @@ void of_gpiochip_remove(struct gpio_chip
 | 
			
		||||
 {
 | 
			
		||||
 	of_node_put(chip->of_node);
 | 
			
		||||
 }
 | 
			
		||||
+
 | 
			
		||||
+static struct of_device_id gpio_export_ids[] = {
 | 
			
		||||
+	{ .compatible = "gpio-export" },
 | 
			
		||||
+	{ /* sentinel */ }
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+static int of_gpio_export_probe(struct platform_device *pdev)
 | 
			
		||||
+{
 | 
			
		||||
+	struct device_node *np = pdev->dev.of_node;
 | 
			
		||||
+	struct device_node *cnp;
 | 
			
		||||
+	u32 val;
 | 
			
		||||
+	int nb = 0;
 | 
			
		||||
+
 | 
			
		||||
+	for_each_child_of_node(np, cnp) {
 | 
			
		||||
+		const char *name = NULL;
 | 
			
		||||
+		int gpio;
 | 
			
		||||
+		bool dmc;
 | 
			
		||||
+		int max_gpio = 1;
 | 
			
		||||
+		int i;
 | 
			
		||||
+
 | 
			
		||||
+		of_property_read_string(cnp, "gpio-export,name", &name);
 | 
			
		||||
+
 | 
			
		||||
+		if (!name)
 | 
			
		||||
+			max_gpio = of_gpio_count(cnp);
 | 
			
		||||
+
 | 
			
		||||
+		for (i = 0; i < max_gpio; i++) {
 | 
			
		||||
+			unsigned flags = 0;
 | 
			
		||||
+			enum of_gpio_flags of_flags;
 | 
			
		||||
+
 | 
			
		||||
+			gpio = of_get_gpio_flags(cnp, i, &of_flags);
 | 
			
		||||
+			if (!gpio_is_valid(gpio))
 | 
			
		||||
+				return gpio;
 | 
			
		||||
+
 | 
			
		||||
+			if (of_flags == OF_GPIO_ACTIVE_LOW)
 | 
			
		||||
+				flags |= GPIOF_ACTIVE_LOW;
 | 
			
		||||
+
 | 
			
		||||
+			if (!of_property_read_u32(cnp, "gpio-export,output", &val))
 | 
			
		||||
+				flags |= val ? GPIOF_OUT_INIT_HIGH : GPIOF_OUT_INIT_LOW;
 | 
			
		||||
+			else
 | 
			
		||||
+				flags |= GPIOF_IN;
 | 
			
		||||
+
 | 
			
		||||
+			if (devm_gpio_request_one(&pdev->dev, gpio, flags, name ? name : of_node_full_name(np)))
 | 
			
		||||
+				continue;
 | 
			
		||||
+
 | 
			
		||||
+			dmc = of_property_read_bool(cnp, "gpio-export,direction_may_change");
 | 
			
		||||
+			gpio_export_with_name(gpio, dmc, name);
 | 
			
		||||
+			nb++;
 | 
			
		||||
+		}
 | 
			
		||||
+	}
 | 
			
		||||
+
 | 
			
		||||
+	dev_info(&pdev->dev, "%d gpio(s) exported\n", nb);
 | 
			
		||||
+
 | 
			
		||||
+	return 0;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
+static struct platform_driver gpio_export_driver = {
 | 
			
		||||
+	.driver		= {
 | 
			
		||||
+		.name		= "gpio-export",
 | 
			
		||||
+		.owner	= THIS_MODULE,
 | 
			
		||||
+		.of_match_table	= of_match_ptr(gpio_export_ids),
 | 
			
		||||
+	},
 | 
			
		||||
+	.probe		= of_gpio_export_probe,
 | 
			
		||||
+};
 | 
			
		||||
+
 | 
			
		||||
+module_platform_driver(gpio_export_driver);
 | 
			
		||||
--- a/drivers/gpio/gpiolib-sysfs.c
 | 
			
		||||
+++ b/drivers/gpio/gpiolib-sysfs.c
 | 
			
		||||
@@ -571,7 +571,7 @@ static struct class gpio_class = {
 | 
			
		||||
  *
 | 
			
		||||
  * Returns zero on success, else an error.
 | 
			
		||||
  */
 | 
			
		||||
-int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 | 
			
		||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name)
 | 
			
		||||
 {
 | 
			
		||||
 	struct gpio_chip	*chip;
 | 
			
		||||
 	struct gpio_device	*gdev;
 | 
			
		||||
@@ -633,6 +633,8 @@ int gpiod_export(struct gpio_desc *desc,
 | 
			
		||||
 	offset = gpio_chip_hwgpio(desc);
 | 
			
		||||
 	if (chip->names && chip->names[offset])
 | 
			
		||||
 		ioname = chip->names[offset];
 | 
			
		||||
+	if (name)
 | 
			
		||||
+		ioname = name;
 | 
			
		||||
 
 | 
			
		||||
 	dev = device_create_with_groups(&gpio_class, &gdev->dev,
 | 
			
		||||
 					MKDEV(0, 0), data, gpio_groups,
 | 
			
		||||
@@ -654,6 +656,12 @@ err_unlock:
 | 
			
		||||
 	gpiod_dbg(desc, "%s: status %d\n", __func__, status);
 | 
			
		||||
 	return status;
 | 
			
		||||
 }
 | 
			
		||||
+EXPORT_SYMBOL_GPL(__gpiod_export);
 | 
			
		||||
+
 | 
			
		||||
+int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 | 
			
		||||
+{
 | 
			
		||||
+	return __gpiod_export(desc, direction_may_change, NULL);
 | 
			
		||||
+}
 | 
			
		||||
 EXPORT_SYMBOL_GPL(gpiod_export);
 | 
			
		||||
 
 | 
			
		||||
 static int match_export(struct device *dev, const void *desc)
 | 
			
		||||
--- a/include/asm-generic/gpio.h
 | 
			
		||||
+++ b/include/asm-generic/gpio.h
 | 
			
		||||
@@ -127,6 +127,12 @@ static inline int gpio_export(unsigned g
 | 
			
		||||
 	return gpiod_export(gpio_to_desc(gpio), direction_may_change);
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
+int __gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
 | 
			
		||||
+static inline int gpio_export_with_name(unsigned gpio, bool direction_may_change, const char *name)
 | 
			
		||||
+{
 | 
			
		||||
+	return __gpiod_export(gpio_to_desc(gpio), direction_may_change, name);
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static inline int gpio_export_link(struct device *dev, const char *name,
 | 
			
		||||
 				   unsigned gpio)
 | 
			
		||||
 {
 | 
			
		||||
--- a/include/linux/gpio/consumer.h
 | 
			
		||||
+++ b/include/linux/gpio/consumer.h
 | 
			
		||||
@@ -668,6 +668,7 @@ static inline void devm_acpi_dev_remove_
 | 
			
		||||
 
 | 
			
		||||
 #if IS_ENABLED(CONFIG_GPIOLIB) && IS_ENABLED(CONFIG_GPIO_SYSFS)
 | 
			
		||||
 
 | 
			
		||||
+int _gpiod_export(struct gpio_desc *desc, bool direction_may_change, const char *name);
 | 
			
		||||
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change);
 | 
			
		||||
 int gpiod_export_link(struct device *dev, const char *name,
 | 
			
		||||
 		      struct gpio_desc *desc);
 | 
			
		||||
@@ -675,6 +676,13 @@ void gpiod_unexport(struct gpio_desc *de
 | 
			
		||||
 
 | 
			
		||||
 #else  /* CONFIG_GPIOLIB && CONFIG_GPIO_SYSFS */
 | 
			
		||||
 
 | 
			
		||||
+static inline int _gpiod_export(struct gpio_desc *desc,
 | 
			
		||||
+			       bool direction_may_change,
 | 
			
		||||
+			       const char *name)
 | 
			
		||||
+{
 | 
			
		||||
+	return -ENOSYS;
 | 
			
		||||
+}
 | 
			
		||||
+
 | 
			
		||||
 static inline int gpiod_export(struct gpio_desc *desc,
 | 
			
		||||
 			       bool direction_may_change)
 | 
			
		||||
 {
 | 
			
		||||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
Index: linux-5.4.137/drivers/usb/serial/option.c
 | 
			
		||||
===================================================================
 | 
			
		||||
--- linux-5.4.137.orig/drivers/usb/serial/option.c
 | 
			
		||||
+++ linux-5.4.137/drivers/usb/serial/option.c
 | 
			
		||||
@@ -241,6 +241,8 @@ static void option_instat_callback(struc
 | 
			
		||||
 #define UBLOX_PRODUCT_R6XX			0x90fa
 | 
			
		||||
 /* These Yuga products use Qualcomm's vendor ID */
 | 
			
		||||
 #define YUGA_PRODUCT_CLM920_NC5			0x9625
 | 
			
		||||
+/* These Meiglink products use Qualcomm's vendor ID */
 | 
			
		||||
+#define MEIGLINK_PRODUCT_SLM750			0xf601
 | 
			
		||||
 
 | 
			
		||||
 #define QUECTEL_VENDOR_ID			0x2c7c
 | 
			
		||||
 /* These Quectel products use Quectel's vendor ID */
 | 
			
		||||
@@ -1102,4 +1104,7 @@ static const struct usb_device_id option
 | 
			
		||||
 	/* u-blox products using Qualcomm vendor ID */
 | 
			
		||||
 	{ USB_DEVICE(QUALCOMM_VENDOR_ID, UBLOX_PRODUCT_R410M),
 | 
			
		||||
 	  .driver_info = RSVD(1) | RSVD(3) },
 | 
			
		||||
+	/* Meiglink products using Qualcomm vendor ID */
 | 
			
		||||
+	// Works OK in case of some issues check macros that are used by Quectel Products
 | 
			
		||||
+	{ USB_DEVICE(QUALCOMM_VENDOR_ID, MEIGLINK_PRODUCT_SLM750)},
 | 
			
		||||
 	/* Quectel products using Quectel vendor ID */
 | 
			
		||||
Index: linux-5.4.137/drivers/net/usb/qmi_wwan.c
 | 
			
		||||
===================================================================
 | 
			
		||||
--- linux-5.4.137.orig/drivers/net/usb/qmi_wwan.c
 | 
			
		||||
+++ linux-5.4.137/drivers/net/usb/qmi_wwan.c
 | 
			
		||||
@@ -1171,6 +1171,7 @@ static const struct usb_device_id produc
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9079, 6)},
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9079, 7)},
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9079, 8)},
 | 
			
		||||
+	{QMI_MATCH_FF_FF_FF(0x05c6, 0xf601)}, 	/* Meiglink SLM750 (in case of issues check if DTR flag setting is enough) */
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9080, 5)},
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9080, 6)},
 | 
			
		||||
 	{QMI_FIXED_INTF(0x05c6, 0x9080, 7)},
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue