Merge "[Refactor mac80211 config to align OpenWRT]"
diff --git a/recipes-connectivity/hostapd/files/hostapd-2G.conf b/recipes-connectivity/hostapd/files/hostapd-2G.conf
index 14cf67b..932e80d 100644
--- a/recipes-connectivity/hostapd/files/hostapd-2G.conf
+++ b/recipes-connectivity/hostapd/files/hostapd-2G.conf
@@ -1,29 +1,71 @@
 driver=nl80211
-interface=wifi0
-bridge=brlan0
-ssid=TurrisOmnia
+logger_syslog=127
+logger_syslog_level=2
+logger_stdout=127
+logger_stdout_level=2
+hw_mode=g
+supported_rates=60 90 120 180 240 360 480 540
+basic_rates=60 120 240
+beacon_int=100
+dtim_period=2
 
 country_code=US
 ieee80211d=1
-hw_mode=g
-beacon_int=100
-channel=11
+channel=1
 
 ieee80211n=1
-ht_capab=[SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1]
+ht_coex=0
+ht_capab=[HT40+][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]
 
-ap_isolate=1
+ieee80211ax=1
+he_su_beamformer=1
+he_mu_beamformer=1
+he_default_pe_duration=4
+he_rts_threshold=1023
+he_mu_edca_qos_info_param_count=0
+he_mu_edca_qos_info_q_ack=0
+he_mu_edca_qos_info_queue_request=0
+he_mu_edca_qos_info_txop_request=0
+he_mu_edca_ac_be_aifsn=8
+he_mu_edca_ac_be_aci=0
+he_mu_edca_ac_be_ecwmin=9
+he_mu_edca_ac_be_ecwmax=10
+he_mu_edca_ac_be_timer=255
+he_mu_edca_ac_bk_aifsn=15
+he_mu_edca_ac_bk_aci=1
+he_mu_edca_ac_bk_ecwmin=9
+he_mu_edca_ac_bk_ecwmax=10
+he_mu_edca_ac_bk_timer=255
+he_mu_edca_ac_vi_ecwmin=5
+he_mu_edca_ac_vi_ecwmax=7
+he_mu_edca_ac_vi_aifsn=5
+he_mu_edca_ac_vi_aci=2
+he_mu_edca_ac_vi_timer=255
+he_mu_edca_ac_vo_aifsn=5
+he_mu_edca_ac_vo_aci=3
+he_mu_edca_ac_vo_ecwmin=5
+he_mu_edca_ac_vo_ecwmax=7
+he_mu_edca_ac_vo_timer=255
+
+interface=wifi0
+ctrl_interface=/var/run/hostapd
+ap_isolate=0
 bss_load_update_period=60
-#chan_util_avg_period=600
+chan_util_avg_period=600
 disassoc_low_ack=1
+skip_inactivity_poll=0
 preamble=1
 wmm_enabled=1
 ignore_broadcast_ssid=0
 uapsd_advertisement_enabled=1
+utf8_ssid=1
+multi_ap=0
 auth_algs=1
-rrm_neighbor_report=1
-bss_transition=1
-macaddr_acl=0
+ssid=Filogic
+bridge=brlan0
+wds_bridge=
+snoop_iface=brlan0
+qos_map_set=0,0,2,16,1,1,255,255,18,22,24,38,40,40,44,46,48,56
 bssid=
 
 #Security parameters
@@ -31,18 +73,10 @@
 wpa_pairwise=CCMP
 wpa_key_mgmt=WPA-PSK
 wpa_passphrase=rdk@1234
-
-#WPS configuration lines
+#WPS configuration lines, but WPS disabled by default
 wps_state=0
 ap_setup_locked=1
 config_methods=virtual_push_button keypad
 wps_pin_requests=/var/run/hostapd_wps_pin_requests.log
 eap_server=1
 
-#Interface for separate control program. hostapd_cli command will use it when trying to connect with hostapd.
-ctrl_interface=/var/run/hostapd
-
-logger_syslog=127
-logger_syslog_level=2
-logger_stdout=127
-logger_stdout_level=2
diff --git a/recipes-connectivity/hostapd/files/hostapd-5G.conf b/recipes-connectivity/hostapd/files/hostapd-5G.conf
index d3b6117..59a2d07 100644
--- a/recipes-connectivity/hostapd/files/hostapd-5G.conf
+++ b/recipes-connectivity/hostapd/files/hostapd-5G.conf
@@ -1,35 +1,76 @@
 driver=nl80211
-interface=wifi1
-bridge=brlan0
-ssid=TurrisOmnia
+logger_syslog=127
+logger_syslog_level=2
+logger_stdout=127
+logger_stdout_level=2
+hw_mode=a
+beacon_int=100
+dtim_period=2
 
 country_code=US
 ieee80211d=1
-hw_mode=a
-beacon_int=100
 channel=36
 
-#ieee80211h=1
+tx_queue_data2_burst=2.0
 ieee80211n=1
-ht_capab=[SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][DSSS_CCK-40]
-
+ht_coex=0
+ht_capab=[HT40+][LDPC][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][MAX-AMSDU-7935]
 ieee80211ac=1
-#vht_oper_chwidth=1
-#vht_oper_centr_freq_seg0_idx=42
-vht_capab=[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][RX-STBC-1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]
+vht_oper_chwidth=1
+vht_oper_centr_freq_seg0_idx=42
+vht_capab=[RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][SU-BEAMFORMER][SU-BEAMFORMEE][MU-BEAMFORMER][MU-BEAMFORMEE][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][RX-STBC-1][SOUNDING-DIMENSION-4][BF-ANTENNA-4][VHT160][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]
 
-ap_isolate=1
+ieee80211ax=1
+he_oper_chwidth=1
+he_oper_centr_freq_seg0_idx=42
+he_su_beamformer=1
+he_mu_beamformer=1
+he_default_pe_duration=4
+he_rts_threshold=1023
+he_mu_edca_qos_info_param_count=0
+he_mu_edca_qos_info_q_ack=0
+he_mu_edca_qos_info_queue_request=0
+he_mu_edca_qos_info_txop_request=0
+he_mu_edca_ac_be_aifsn=8
+he_mu_edca_ac_be_aci=0
+he_mu_edca_ac_be_ecwmin=9
+he_mu_edca_ac_be_ecwmax=10
+he_mu_edca_ac_be_timer=255
+he_mu_edca_ac_bk_aifsn=15
+he_mu_edca_ac_bk_aci=1
+he_mu_edca_ac_bk_ecwmin=9
+he_mu_edca_ac_bk_ecwmax=10
+he_mu_edca_ac_bk_timer=255
+he_mu_edca_ac_vi_ecwmin=5
+he_mu_edca_ac_vi_ecwmax=7
+he_mu_edca_ac_vi_aifsn=5
+he_mu_edca_ac_vi_aci=2
+he_mu_edca_ac_vi_timer=255
+he_mu_edca_ac_vo_aifsn=5
+he_mu_edca_ac_vo_aci=3
+he_mu_edca_ac_vo_ecwmin=5
+he_mu_edca_ac_vo_ecwmax=7
+he_mu_edca_ac_vo_timer=255
+
+interface=wifi1
+ctrl_interface=/var/run/hostapd
+ap_isolate=0
 bss_load_update_period=60
-#chan_util_avg_period=600
+chan_util_avg_period=600
 disassoc_low_ack=1
+skip_inactivity_poll=0
 preamble=1
 wmm_enabled=1
 ignore_broadcast_ssid=0
 uapsd_advertisement_enabled=1
+utf8_ssid=1
+multi_ap=0
 auth_algs=1
-rrm_neighbor_report=1
-bss_transition=1
-macaddr_acl=0
+ssid=Filogic_5
+bridge=brlan0
+wds_bridge=
+snoop_iface=brlan0
+qos_map_set=0,0,2,16,1,1,255,255,18,22,24,38,40,40,44,46,48,56
 bssid=
 
 #Security parameters
@@ -37,7 +78,6 @@
 wpa_pairwise=CCMP
 wpa_key_mgmt=WPA-PSK
 wpa_passphrase=rdk@1234
-
 #WPS configuration lines, but WPS disabled by default
 wps_state=0
 ap_setup_locked=1
@@ -45,10 +85,3 @@
 wps_pin_requests=/var/run/hostapd_wps_pin_requests.log
 eap_server=1
 
-#Interface for separate control program. hostapd_cli command will use it when trying to connect with hostapd.
-ctrl_interface=/var/run/hostapd
-
-logger_syslog=127
-logger_syslog_level=2
-logger_stdout=127
-logger_stdout_level=2
diff --git a/recipes-devtools/mtd/files/COPYING b/recipes-devtools/mtd/files/COPYING
new file mode 100644
index 0000000..7975bdb
--- /dev/null
+++ b/recipes-devtools/mtd/files/COPYING
@@ -0,0 +1 @@
+Copyright (c) Mediatek 2020
diff --git a/recipes-devtools/mtd/files/src/Makefile b/recipes-devtools/mtd/files/src/Makefile
new file mode 100644
index 0000000..520715c
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/Makefile
@@ -0,0 +1,30 @@
+#SPDX-License-Identifier: GPL-2.0
+
+#CC = gcc
+#CFLAGS += -Wall
+#LDFLAGS += -lubox
+
+obj = mtd.o jffs2.o crc32.o md5.o
+obj.seama = seama.o md5.o
+obj.wrg = wrg.o md5.o
+obj.wrgg = wrgg.o md5.o
+obj.tpl = tpl_ramips_recoveryflag.o
+obj.ath79 = $(obj.seama) $(obj.wrgg)
+obj.gemini = $(obj.wrgg)
+obj.brcm = trx.o
+obj.bcm47xx = $(obj.brcm)
+obj.bcm53xx = $(obj.brcm) $(obj.seama)
+obj.bcm63xx = imagetag.o
+obj.ramips = $(obj.seama) $(obj.tpl) $(obj.wrg) linksys_bootcount.o
+obj.mvebu = linksys_bootcount.o
+obj.kirkwood = linksys_bootcount.o
+obj.ipq806x = linksys_bootcount.o
+obj.ipq40xx = linksys_bootcount.o
+
+ifdef FIS_SUPPORT
+  obj += fis.o
+endif
+
+mtd: $(obj) $(obj.$(TARGET))
+clean:
+	rm -f *.o jffs2
diff --git a/recipes-devtools/mtd/files/src/crc32.c b/recipes-devtools/mtd/files/src/crc32.c
new file mode 100644
index 0000000..cb5c5de
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/crc32.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+#include <stdint.h>
+
+const uint32_t crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
diff --git a/recipes-devtools/mtd/files/src/crc32.h b/recipes-devtools/mtd/files/src/crc32.h
new file mode 100644
index 0000000..2ee1e33
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/crc32.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef CRC32_H
+#define CRC32_H
+
+#include <stdint.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+static inline uint32_t
+crc32(uint32_t val, const void *ss, int len)
+{
+	const unsigned char *s = ss;
+	while (--len >= 0)
+		val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+	return val;
+}
+
+static inline unsigned int crc32buf(char *buf, size_t len)
+{
+	return crc32(0xFFFFFFFF, buf, len);
+}
+
+
+
+#endif
diff --git a/recipes-devtools/mtd/files/src/fis.c b/recipes-devtools/mtd/files/src/fis.c
new file mode 100644
index 0000000..bca985a
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/fis.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * FIS table updating code for mtd
+ *
+ * Copyright (C) 2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <sys/mman.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+#include "crc32.h"
+#include "mtd.h"
+#include "fis.h"
+
+struct fis_image_hdr {
+	unsigned char name[16];
+	uint32_t flash_base;
+	uint32_t mem_base;
+	uint32_t size;
+	uint32_t entry_point;
+	uint32_t data_length;
+} __attribute__((packed));
+
+struct fis_image_crc {
+	uint32_t desc;
+	uint32_t file;
+} __attribute__((packed));
+
+struct fis_image_desc {
+	struct fis_image_hdr hdr;
+	char _pad[256 - sizeof(struct fis_image_hdr) - sizeof(struct fis_image_crc)];
+	struct fis_image_crc crc;
+} __attribute__((packed));
+
+static int fis_fd = -1;
+static struct fis_image_desc *fis_desc;
+static int fis_erasesize = 0;
+
+static void
+fis_close(void)
+{
+	if (fis_desc)
+		munmap(fis_desc, fis_erasesize);
+
+	if (fis_fd >= 0)
+		close(fis_fd);
+
+	fis_fd = -1;
+	fis_desc = NULL;
+}
+
+static struct fis_image_desc *
+fis_open(void)
+{
+	struct fis_image_desc *desc;
+
+	if (fis_fd >= 0)
+		fis_close();
+
+	fis_fd = mtd_check_open("FIS directory");
+	if (fis_fd < 0)
+		goto error;
+
+	close(fis_fd);
+	fis_fd = mtd_open("FIS directory", true);
+	if (fis_fd < 0)
+		goto error;
+
+	fis_erasesize = erasesize;
+	desc = mmap(NULL, erasesize, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_LOCKED, fis_fd, 0);
+	if (desc == MAP_FAILED)
+		goto error;
+
+	fis_desc = desc;
+	return desc;
+
+error:
+	fis_close();
+	return NULL;
+}
+
+int
+fis_validate(struct fis_part *old, int n_old, struct fis_part *new, int n_new)
+{
+	struct fis_image_desc *desc;
+	void *end;
+	int found = 0;
+	int i;
+
+	desc = fis_open();
+	if (!desc)
+		return -1;
+
+	for (i = 0; i < n_new - 1; i++) {
+		if (!new[i].size) {
+			fprintf(stderr, "FIS error: only the last partition can detect the size automatically\n");
+			i = -1;
+			goto done;
+		}
+	}
+
+	end = desc;
+	end = (char *) end + fis_erasesize;
+	while ((void *) desc < end) {
+		if (!desc->hdr.name[0] || (desc->hdr.name[0] == 0xff))
+			break;
+
+		for (i = 0; i < n_old; i++) {
+			if (!strncmp((char *) desc->hdr.name, (char *) old[i].name, sizeof(desc->hdr.name))) {
+				found++;
+				goto next;
+			}
+		}
+next:
+		desc++;
+		continue;
+	}
+
+	if (found == n_old)
+		i = 1;
+	else
+		i = -1;
+
+done:
+	fis_close();
+	return i;
+}
+
+int
+fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new)
+{
+	struct fis_image_desc *first = NULL;
+	struct fis_image_desc *last = NULL;
+	struct fis_image_desc *first_fb = NULL;
+	struct fis_image_desc *last_fb = NULL;
+	struct fis_image_desc *desc;
+	struct fis_part *part;
+	uint32_t offset = 0, size = 0;
+	char *start, *end, *tmp;
+	int i;
+
+	desc = fis_open();
+	if (!desc)
+		return -1;
+
+	if (!quiet)
+		fprintf(stderr, "Updating FIS table... \n");
+
+	start = (char *) desc;
+	end = (char *) desc + fis_erasesize;
+	while ((char *) desc < end) {
+		if (!desc->hdr.name[0] || (desc->hdr.name[0] == 0xff))
+			break;
+
+		/* update max offset */
+		if (offset < desc->hdr.flash_base)
+			offset = desc->hdr.flash_base;
+
+		for (i = 0; i < n_old; i++) {
+			if (!strncmp((char *) desc->hdr.name, (char *) old[i].name, sizeof(desc->hdr.name))) {
+				last = desc;
+				if (!first)
+					first = desc;
+				break;
+			}
+		}
+		desc++;
+	}
+	desc--;
+
+	first_fb = first;
+	last_fb = last;
+
+	if (first_fb->hdr.flash_base > last_fb->hdr.flash_base) {
+		first_fb = last;
+		last_fb = first;
+	}
+
+	/* determine size of available space */
+	desc = (struct fis_image_desc *) start;
+	while ((char *) desc < end) {
+		if (!desc->hdr.name[0] || (desc->hdr.name[0] == 0xff))
+			break;
+
+		if (desc->hdr.flash_base > last_fb->hdr.flash_base &&
+		    desc->hdr.flash_base < offset)
+			offset = desc->hdr.flash_base;
+
+		desc++;
+	}
+	desc--;
+
+	size = offset - first_fb->hdr.flash_base;
+
+	last++;
+	desc = first + n_new;
+	offset = first_fb->hdr.flash_base;
+
+	if (desc != last) {
+		if (desc > last)
+			tmp = (char *) desc;
+		else
+			tmp = (char *) last;
+
+		memmove(desc, last, end - tmp);
+		if (desc < last) {
+			tmp = end - (last - desc) * sizeof(struct fis_image_desc);
+			memset(tmp, 0xff, tmp - end);
+		}
+	}
+
+	for (part = new, desc = first; desc < first + n_new; desc++, part++) {
+		memset(desc, 0, sizeof(struct fis_image_desc));
+		memcpy(desc->hdr.name, part->name, sizeof(desc->hdr.name));
+		desc->crc.desc = 0;
+		desc->crc.file = part->crc;
+
+		desc->hdr.flash_base = offset;
+		desc->hdr.mem_base = part->loadaddr;
+		desc->hdr.entry_point = part->loadaddr;
+		desc->hdr.size = (part->size > 0) ? part->size : size;
+		desc->hdr.data_length = (part->length > 0) ? part->length :
+								desc->hdr.size;
+		offset += desc->hdr.size;
+		size -= desc->hdr.size;
+	}
+
+	msync(fis_desc, fis_erasesize, MS_SYNC|MS_INVALIDATE);
+	fis_close();
+
+	return 0;
+}
diff --git a/recipes-devtools/mtd/files/src/fis.h b/recipes-devtools/mtd/files/src/fis.h
new file mode 100644
index 0000000..9135cab
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/fis.h
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef __FIS_H
+#define __FIS_H
+
+struct fis_part {
+	unsigned char name[16];
+	uint32_t offset;
+	uint32_t loadaddr;
+	uint32_t size;
+	uint32_t length;
+	uint32_t crc;
+};
+
+int fis_validate(struct fis_part *old, int n_old, struct fis_part *new, int n_new);
+int fis_remap(struct fis_part *old, int n_old, struct fis_part *new, int n_new);
+
+#endif
diff --git a/recipes-devtools/mtd/files/src/imagetag.c b/recipes-devtools/mtd/files/src/imagetag.c
new file mode 100644
index 0000000..f592eda
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/imagetag.c
@@ -0,0 +1,412 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * imagetag.c
+ *
+ * Copyright (C) 2005 Mike Baker
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyrigth (C) 2010 Daniel Dickinson <openwrt@cshore.neomailbox.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include "mtd.h"
+#include "crc32.h"
+
+#define TAGVER_LEN		4	/* Length of Tag Version */
+#define TAGLAYOUT_LEN		4	/* Length of FlashLayoutVer */
+#define SIG1_LEN		20	/* Company Signature 1 Length */
+#define SIG2_LEN		14	/* Company Signature 2 Length */
+#define BOARDID_LEN		16	/* Length of BoardId */
+#define ENDIANFLAG_LEN		2	/* Endian Flag Length */
+#define CHIPID_LEN		6	/* Chip Id Length */
+#define IMAGE_LEN		10	/* Length of Length Field */
+#define ADDRESS_LEN		12	/* Length of Address field */
+#define DUALFLAG_LEN		2	/* Dual Image flag Length */
+#define INACTIVEFLAG_LEN	2	/* Inactie Flag Length */
+#define RSASIG_LEN		20	/* Length of RSA Signature in tag */
+#define TAGINFO1_LEN		30	/* Length of vendor information field1 in tag */
+#define FLASHLAYOUTVER_LEN	4	/* Length of Flash Layout Version String tag */
+#define TAGINFO2_LEN		16	/* Length of vendor information field2 in tag */
+#define ALTTAGINFO_LEN		54	/* Alternate length for vendor information; Pirelli */
+
+#define NUM_PIRELLI		2
+#define IMAGETAG_CRC_START	0xFFFFFFFF
+
+#define PIRELLI_BOARDS { \
+	"AGPF-S0", \
+	"DWV-S0", \
+}
+/*
+ * The broadcom firmware assumes the rootfs starts the image,
+ * therefore uses the rootfs start (flash_image_address)
+ * to determine where to flash the image.  Since we have the kernel first
+ * we have to give it the kernel address, but the crc uses the length
+ * associated with this address (root_length), which is added to the kernel
+ * length (kernel_length) to determine the length of image to flash and thus
+ * needs to be rootfs + deadcode (jffs2 EOF marker)
+*/
+
+struct bcm_tag {
+	/* 0-3: Version of the image tag */
+	char tag_version[TAGVER_LEN];
+	/* 4-23: Company Line 1 */
+	char sig_1[SIG1_LEN];
+	/*  24-37: Company Line 2 */
+	char sig_2[SIG2_LEN];
+	/* 38-43: Chip this image is for */
+	char chip_id[CHIPID_LEN];
+	/* 44-59: Board name */
+	char board_id[BOARDID_LEN];
+	/* 60-61: Map endianness -- 1 BE 0 LE */
+	char big_endian[ENDIANFLAG_LEN];
+	/* 62-71: Total length of image */
+	char total_length[IMAGE_LEN];
+	/* 72-83: Address in memory of CFE */
+	char cfe__address[ADDRESS_LEN];
+	/* 84-93: Size of CFE */
+	char cfe_length[IMAGE_LEN];
+	/* 94-105: Address in memory of image start
+	 * (kernel for OpenWRT, rootfs for stock firmware)
+	 */
+	char flash_image_start[ADDRESS_LEN];
+	/* 106-115: Size of rootfs */
+	char root_length[IMAGE_LEN];
+	/* 116-127: Address in memory of kernel */
+	char kernel_address[ADDRESS_LEN];
+	/* 128-137: Size of kernel */
+	char kernel_length[IMAGE_LEN];
+	/* 138-139: Unused at the moment */
+	char dual_image[DUALFLAG_LEN];
+	/* 140-141: Unused at the moment */
+	char inactive_flag[INACTIVEFLAG_LEN];
+	/* 142-161: RSA Signature (not used; some vendors may use this) */
+	char rsa_signature[RSASIG_LEN];
+	/* 162-191: Compilation and related information (not used in OpenWrt) */
+	char information1[TAGINFO1_LEN];
+	/* 192-195: Version flash layout */
+	char flash_layout_ver[FLASHLAYOUTVER_LEN];
+	/* 196-199: kernel+rootfs CRC32 */
+	__u32 fskernel_crc;
+	/* 200-215: Unused except on Alice Gate where is is information */
+	char information2[TAGINFO2_LEN];
+	/* 216-219: CRC32 of image less imagetag (kernel for Alice Gate) */
+	__u32 image_crc;
+	/* 220-223: CRC32 of rootfs partition */
+	__u32 rootfs_crc;
+	/* 224-227: CRC32 of kernel partition */
+	__u32 kernel_crc;
+	/* 228-231: Image sequence number */
+	char image_sequence[4];
+	/* 222-235: Openwrt: real rootfs length */
+	__u32 real_rootfs_length;
+	/* 236-239: CRC32 of header excluding last 20 bytes */
+	__u32 header_crc;
+	/* 240-255: Unused at present */
+	char reserved2[16];
+};
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+#define CRC_START 0xFFFFFFFF
+
+static uint32_t strntoul(char *str, char **endptr, int base, size_t len) {
+  char *newstr;
+  uint32_t res = 0;
+
+  newstr = calloc(len + 1, sizeof(char));
+  if (newstr) {
+	strncpy(newstr, str, len); 
+	res = strtoul(newstr, endptr, base);
+	free(newstr);
+  }
+  return res;
+}
+
+uint32_t compute_crc32(uint32_t crc, off_t start, size_t compute_len, int fd)
+{
+	uint8_t readbuf[1024];
+	ssize_t res;
+	off_t offset = start;
+
+	/* Read a buffer's worth of bytes  */
+	while (fd && (compute_len >= sizeof(readbuf))) {
+		res = pread(fd, readbuf, sizeof(readbuf), offset);
+		crc = crc32(crc, readbuf, res);
+		compute_len = compute_len - res;
+		offset += res;
+	}
+
+	/* Less than buffer-size bytes remains, read compute_len bytes */
+	if (fd && (compute_len > 0)) {
+	  res = pread(fd, readbuf, compute_len, offset);
+	  crc = crc32(crc, readbuf, res);
+	}
+
+	return crc;
+}
+
+int
+trx_fixup(int fd, const char *name)
+{
+	struct mtd_info_user mtdInfo;
+	unsigned long len;
+	void *ptr, *scan;
+	int bfd;
+	struct bcm_tag *tag;
+	ssize_t res;
+	uint32_t cfelen, imagelen, imagestart, rootfslen;
+	uint32_t imagecrc, rootfscrc, headercrc;
+	uint32_t offset = 0;
+	cfelen = imagelen = imagestart = imagecrc = rootfscrc = headercrc = rootfslen = 0;
+
+
+	if (ioctl(fd, MEMGETINFO, &mtdInfo) < 0) {
+		fprintf(stderr, "Failed to get mtd info\n");
+		goto err;
+	}
+
+	len = mtdInfo.size;
+	if (mtdInfo.size <= 0) {
+		fprintf(stderr, "Invalid MTD device size\n");
+		goto err;
+	}
+
+	bfd = mtd_open(name, true);
+	ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, bfd, 0);
+	if (!ptr || (ptr == (void *) -1)) {
+		perror("mmap");
+		goto err1;
+	}
+
+	tag = (struct bcm_tag *) (ptr);
+
+	cfelen = strntoul(&tag->cfe_length[0], NULL, 10, IMAGE_LEN);
+	if (cfelen) {
+	  fprintf(stderr, "Non-zero CFE length.  This is currently unsupported.\n");
+	  exit(1);
+	}
+
+	headercrc = compute_crc32(CRC_START, offset, offsetof(struct bcm_tag, header_crc), fd);
+	if (headercrc != *(uint32_t *)(&tag->header_crc)) {
+		fprintf(stderr, "Tag verify failed.  This may not be a valid image.\n");
+		exit(1);
+	}
+
+	sprintf(&tag->root_length[0], "%u", 0);
+	strncpy(&tag->total_length[0], &tag->kernel_length[0], IMAGE_LEN);
+
+	imagestart = sizeof(tag);
+	memcpy(&tag->image_crc, &tag->kernel_crc, sizeof(uint32_t));
+	memcpy(&tag->fskernel_crc, &tag->kernel_crc, sizeof(uint32_t));
+	rootfscrc = CRC_START;
+	memcpy(&tag->rootfs_crc, &rootfscrc, sizeof(uint32_t));
+	headercrc = crc32(CRC_START, tag, offsetof(struct bcm_tag, header_crc));
+	memcpy(&tag->header_crc, &headercrc, sizeof(uint32_t));
+
+	msync(ptr, sizeof(struct bcm_tag), MS_SYNC|MS_INVALIDATE);
+	munmap(ptr, len);
+	close(bfd);
+	return 0;
+
+err1:
+	close(bfd);
+err:
+	fprintf(stderr, "Error fixing up imagetag header\n");
+	return -1;
+}
+
+
+int
+trx_check(int imagefd, const char *mtd, char *buf, int *len)
+{
+    struct bcm_tag *tag = (const struct bcm_tag *) buf;
+	int fd;
+	uint32_t headerCRC;
+	uint32_t imageLen;
+
+	if (strcmp(mtd, "linux") != 0)
+		return 1;
+
+	*len = read(imagefd, buf, sizeof(struct bcm_tag));
+	if (*len < sizeof(struct bcm_tag)) {
+		fprintf(stdout, "Could not get image header, file too small (%d bytes)\n", *len);
+		return 0;
+	}
+	headerCRC = crc32buf(buf, offsetof(struct bcm_tag, header_crc));
+	if (*(uint32_t *)(&tag->header_crc) != headerCRC) {
+  
+	  if (quiet < 2) {
+		fprintf(stderr, "Bad header CRC got %08x, calculated %08x\n",
+				*(uint32_t *)(&tag->header_crc), headerCRC);
+		fprintf(stderr, "This is not the correct file format; refusing to flash.\n"
+				"Please specify the correct file or use -f to force.\n");
+	  }
+	  return 0;
+	}
+
+	/* check if image fits to mtd device */
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	imageLen = strntoul(&tag->total_length[0], NULL, 10, IMAGE_LEN);
+	
+	if(mtdsize < imageLen) {
+		fprintf(stderr, "Image too big for partition: %s\n", mtd);
+		close(fd);
+		return 0;
+	}
+
+	close(fd);
+	return 1;
+}
+
+int
+mtd_fixtrx(const char *mtd, size_t offset, size_t data_size)
+{
+	int fd;
+	struct bcm_tag *tag;
+	char *buf;
+	ssize_t res;
+	size_t block_offset;
+	uint32_t cfelen, imagelen, imagestart, rootfslen;
+	uint32_t imagecrc, rootfscrc, headercrc;
+	cfelen = imagelen = imagestart = imagecrc = rootfscrc = headercrc = rootfslen = 0;
+
+	if (data_size)
+		fprintf(stderr, "Specifying data size in unsupported for imagetag\n");
+
+	if (quiet < 2)
+		fprintf(stderr, "Trying to fix trx header in %s at 0x%x...\n", mtd, offset);
+
+	block_offset = offset & ~(erasesize - 1);
+	offset -= block_offset;
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	if (block_offset + erasesize > mtdsize) {
+		fprintf(stderr, "Offset too large, device size 0x%x\n", mtdsize);
+		exit(1);
+	}
+
+	buf = malloc(erasesize);
+	if (!buf) {
+		perror("malloc");
+		exit(1);
+	}
+
+	res = pread(fd, buf, erasesize, block_offset);
+	if (res != erasesize) {
+		perror("pread");
+		exit(1);
+	}
+
+	tag = (struct bcm_tag *) (buf + offset);
+
+	cfelen = strntoul(tag->cfe_length, NULL, 10, IMAGE_LEN);
+	if (cfelen) {
+	  fprintf(stderr, "Non-zero CFE length.  This is currently unsupported.\n");
+	  exit(1);
+	}
+
+	if (quiet < 2) {
+	  fprintf(stderr, "Verifying we actually have an imagetag.\n");
+	}
+
+	headercrc = compute_crc32(CRC_START, offset, offsetof(struct bcm_tag, header_crc), fd);
+	if (headercrc != *(uint32_t *)(&tag->header_crc)) {
+		fprintf(stderr, "Tag verify failed.  This may not be a valid image.\n");
+		exit(1);
+	}
+
+	if (quiet < 2) {
+	  fprintf(stderr, "Checking current fixed status.\n");
+	}
+
+	rootfslen = strntoul(&tag->root_length[0], NULL, 10, IMAGE_LEN);
+	if (rootfslen == 0) {
+	  if (quiet < 2) 
+		fprintf(stderr, "Header already fixed, exiting\n");
+	  close(fd);
+	  return 0;
+	}
+
+	if (quiet < 2) {
+	  fprintf(stderr, "Setting root length to 0.\n");
+	}
+
+	sprintf(&tag->root_length[0], "%u", 0);
+	strncpy(&tag->total_length[0], &tag->kernel_length[0], IMAGE_LEN);
+
+	if (quiet < 2) {
+	  fprintf(stderr, "Recalculating CRCs.\n");
+	}
+
+	imagestart = sizeof(tag);
+	memcpy(&tag->image_crc, &tag->kernel_crc, sizeof(uint32_t));
+	memcpy(&tag->fskernel_crc, &tag->kernel_crc, sizeof(uint32_t));
+	rootfscrc = CRC_START;
+	memcpy(&tag->rootfs_crc, &rootfscrc, sizeof(uint32_t));
+	headercrc = crc32(CRC_START, tag, offsetof(struct bcm_tag, header_crc));
+	memcpy(&tag->header_crc, &headercrc, sizeof(uint32_t));
+
+	if (quiet < 2) {
+	  fprintf(stderr, "Erasing imagetag block\n");
+	}
+
+	if (mtd_erase_block(fd, block_offset)) {
+		fprintf(stderr, "Can't erase block at 0x%x (%s)\n", block_offset, strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2) {
+	  fprintf(stderr, "New image crc32: 0x%x, rewriting block\n", 
+			  *(uint32_t *)(&tag->image_crc));
+	  fprintf(stderr, "New header crc32: 0x%x, rewriting block\n", headercrc);  
+	}
+
+	if (pwrite(fd, buf, erasesize, block_offset) != erasesize) {
+		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Done.\n");
+
+	close (fd);
+	sync();
+	return 0;
+
+}
diff --git a/recipes-devtools/mtd/files/src/jffs2.c b/recipes-devtools/mtd/files/src/jffs2.c
new file mode 100644
index 0000000..92fcfa3
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/jffs2.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * jffs2 on-disk structure generator for mtd
+ *
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * Based on:
+ *   JFFS2 -- Journalling Flash File System, Version 2.
+ *   Copyright © 2001-2007 Red Hat, Inc.
+ *   Created by David Woodhouse <dwmw2@infradead.org>
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <endian.h>
+#include "jffs2.h"
+#include "crc32.h"
+#include "mtd.h"
+
+#define PAD(x) (((x)+3)&~3)
+
+#if BYTE_ORDER == BIG_ENDIAN
+# define CLEANMARKER "\x19\x85\x20\x03\x00\x00\x00\x0c\xf0\x60\xdc\x98"
+#else
+# define CLEANMARKER "\x85\x19\x03\x20\x0c\x00\x00\x00\xb1\xb0\x1e\xe4"
+#endif
+
+static int last_ino = 0;
+static int last_version = 0;
+static char *buf = NULL;
+static int ofs = 0;
+static int outfd = -1;
+static int mtdofs = 0;
+static int target_ino = 0;
+
+static void prep_eraseblock(void);
+
+static void pad(int size)
+{
+	if ((ofs % size == 0) && (ofs < erasesize))
+		return;
+
+	if (ofs < erasesize) {
+		memset(buf + ofs, 0xff, (size - (ofs % size)));
+		ofs += (size - (ofs % size));
+	}
+	ofs = ofs % erasesize;
+	if (ofs == 0) {
+		while (mtd_block_is_bad(outfd, mtdofs) && (mtdofs < mtdsize)) {
+			if (!quiet)
+				fprintf(stderr, "\nSkipping bad block at 0x%08x   ", mtdofs);
+
+			mtdofs += erasesize;
+
+			/* Move the file pointer along over the bad block. */
+			lseek(outfd, erasesize, SEEK_CUR);
+		}
+		mtd_erase_block(outfd, mtdofs);
+		write(outfd, buf, erasesize);
+		mtdofs += erasesize;
+	}
+}
+
+static inline int rbytes(void)
+{
+	return erasesize - (ofs % erasesize);
+}
+
+static inline void add_data(char *ptr, int len)
+{
+	if (ofs + len > erasesize) {
+		pad(erasesize);
+		prep_eraseblock();
+	}
+	memcpy(buf + ofs, ptr, len);
+	ofs += len;
+}
+
+static void prep_eraseblock(void)
+{
+	if (ofs > 0)
+		return;
+
+	add_data(CLEANMARKER, sizeof(CLEANMARKER) - 1);
+}
+
+static int add_dirent(const char *name, const char type, int parent)
+{
+	struct jffs2_raw_dirent *de;
+
+	if (ofs - erasesize < sizeof(struct jffs2_raw_dirent) + strlen(name))
+		pad(erasesize);
+
+	prep_eraseblock();
+	last_ino++;
+	memset(buf + ofs, 0, sizeof(struct jffs2_raw_dirent));
+	de = (struct jffs2_raw_dirent *) (buf + ofs);
+
+	de->magic = JFFS2_MAGIC_BITMASK;
+	de->nodetype = JFFS2_NODETYPE_DIRENT;
+	de->type = type;
+	de->name_crc = crc32(0, name, strlen(name));
+	de->ino = last_ino++;
+	de->pino = parent;
+	de->totlen = sizeof(*de) + strlen(name);
+	de->hdr_crc = crc32(0, (void *) de, sizeof(struct jffs2_unknown_node) - 4);
+	de->version = last_version++;
+	de->mctime = 0;
+	de->nsize = strlen(name);
+	de->node_crc = crc32(0, (void *) de, sizeof(*de) - 8);
+	memcpy(de->name, name, strlen(name));
+
+	ofs += sizeof(struct jffs2_raw_dirent) + de->nsize;
+	pad(4);
+
+	return de->ino;
+}
+
+static int add_dir(const char *name, int parent)
+{
+	struct jffs2_raw_inode ri;
+	int inode;
+
+	inode = add_dirent(name, IFTODT(S_IFDIR), parent);
+
+	if (rbytes() < sizeof(ri))
+		pad(erasesize);
+	prep_eraseblock();
+
+	memset(&ri, 0, sizeof(ri));
+	ri.magic = JFFS2_MAGIC_BITMASK;
+	ri.nodetype = JFFS2_NODETYPE_INODE;
+	ri.totlen = sizeof(ri);
+	ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+
+	ri.ino = inode;
+	ri.mode = S_IFDIR | 0755;
+	ri.uid = ri.gid = 0;
+	ri.atime = ri.ctime = ri.mtime = 0;
+	ri.isize = ri.csize = ri.dsize = 0;
+	ri.version = 1;
+	ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+	ri.data_crc = 0;
+
+	add_data((char *) &ri, sizeof(ri));
+	pad(4);
+	return inode;
+}
+
+static void add_file(const char *name, int parent)
+{
+	int inode, f_offset = 0, fd;
+	struct jffs2_raw_inode ri;
+	struct stat st;
+	char wbuf[4096];
+	const char *fname;
+
+	if (stat(name, &st)) {
+		fprintf(stderr, "File %s does not exist\n", name);
+		return;
+	}
+
+	fname = strrchr(name, '/');
+	if (fname)
+		fname++;
+	else
+		fname = name;
+
+	inode = add_dirent(fname, IFTODT(S_IFREG), parent);
+	memset(&ri, 0, sizeof(ri));
+	ri.magic = JFFS2_MAGIC_BITMASK;
+	ri.nodetype = JFFS2_NODETYPE_INODE;
+
+	ri.ino = inode;
+	ri.mode = st.st_mode;
+	ri.uid = ri.gid = 0;
+	ri.atime = st.st_atime;
+	ri.ctime = st.st_ctime;
+	ri.mtime = st.st_mtime;
+	ri.isize = st.st_size;
+	ri.compr = 0;
+	ri.usercompr = 0;
+
+	fd = open(name, 0);
+	if (fd < 0) {
+		fprintf(stderr, "File %s does not exist\n", name);
+		return;
+	}
+
+	for (;;) {
+		int len = 0;
+
+		for (;;) {
+			len = rbytes() - sizeof(ri);
+			if (len > 128)
+				break;
+
+			pad(erasesize);
+			prep_eraseblock();
+		}
+
+		if (len > sizeof(wbuf))
+			len = sizeof(wbuf);
+
+		len = read(fd, wbuf, len);
+		if (len <= 0)
+			break;
+
+		ri.totlen = sizeof(ri) + len;
+		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node) - 4);
+		ri.version = ++last_version;
+		ri.offset = f_offset;
+		ri.csize = ri.dsize = len;
+		ri.node_crc = crc32(0, &ri, sizeof(ri) - 8);
+		ri.data_crc = crc32(0, wbuf, len);
+		f_offset += len;
+		add_data((char *) &ri, sizeof(ri));
+		add_data(wbuf, len);
+		pad(4);
+		prep_eraseblock();
+	}
+
+	close(fd);
+}
+
+int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename)
+{
+	outfd = fd;
+	mtdofs = ofs;
+
+	buf = malloc(erasesize);
+	target_ino = 1;
+	if (!last_ino)
+		last_ino = 1;
+	add_file(filename, target_ino);
+	pad(erasesize);
+
+	/* add eof marker, pad to eraseblock size and write the data */
+	add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
+	pad(erasesize);
+	free(buf);
+
+	return (mtdofs - ofs);
+}
+
+void mtd_parse_jffs2data(const char *buf, const char *dir)
+{
+	struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
+	unsigned int ofs = 0;
+
+	while (ofs < erasesize) {
+		node = (struct jffs2_unknown_node *) (buf + ofs);
+		if (node->magic != 0x1985)
+			break;
+
+		ofs += PAD(node->totlen);
+		if (node->nodetype == JFFS2_NODETYPE_DIRENT) {
+			struct jffs2_raw_dirent *de = (struct jffs2_raw_dirent *) node;
+
+			/* is this the right directory name and is it a subdirectory of / */
+			if (*dir && (de->pino == 1) && !strncmp((char *) de->name, dir, de->nsize))
+				target_ino = de->ino;
+
+			/* store the last inode and version numbers for adding extra files */
+			if (last_ino < de->ino)
+				last_ino = de->ino;
+			if (last_version < de->version)
+				last_version = de->version;
+		}
+	}
+}
+
+int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir)
+{
+	int err = -1, fdeof = 0;
+
+	outfd = mtd_check_open(mtd);
+	if (outfd < 0)
+		return -1;
+
+	if (quiet < 2)
+		fprintf(stderr, "Appending %s to jffs2 partition %s\n", filename, mtd);
+	
+	buf = malloc(erasesize);
+	if (!buf) {
+		fprintf(stderr, "Out of memory!\n");
+		goto done;
+	}
+
+	if (!*dir)
+		target_ino = 1;
+
+	/* parse the structure of the jffs2 first
+	 * locate the directory that the file is going to be placed in */
+	for(;;) {
+		struct jffs2_unknown_node *node = (struct jffs2_unknown_node *) buf;
+
+		if (read(outfd, buf, erasesize) != erasesize) {
+			fdeof = 1;
+			break;
+		}
+		mtdofs += erasesize;
+
+		if (node->magic == 0x8519) {
+			fprintf(stderr, "Error: wrong endianness filesystem\n");
+			goto done;
+		}
+
+		/* assume  no magic == end of filesystem
+		 * the filesystem will probably end with be32(0xdeadc0de) */
+		if (node->magic != 0x1985)
+			break;
+
+		mtd_parse_jffs2data(buf, dir);
+	}
+
+	if (fdeof) {
+		fprintf(stderr, "Error: No room for additional data\n");
+		goto done;
+	}
+
+	/* jump back one eraseblock */
+	mtdofs -= erasesize;
+	lseek(outfd, mtdofs, SEEK_SET);
+
+	ofs = 0;
+
+	if (!last_ino)
+		last_ino = 1;
+
+	if (!target_ino)
+		target_ino = add_dir(dir, 1);
+
+	add_file(filename, target_ino);
+	pad(erasesize);
+
+	/* add eof marker, pad to eraseblock size and write the data */
+	add_data(JFFS2_EOF, sizeof(JFFS2_EOF) - 1);
+	pad(erasesize);
+
+	err = 0;
+
+	if (trx_fixup) {
+	  trx_fixup(outfd, mtd);
+	}
+
+done:
+	close(outfd);
+	if (buf)
+		free(buf);
+
+	return err;
+}
diff --git a/recipes-devtools/mtd/files/src/jffs2.h b/recipes-devtools/mtd/files/src/jffs2.h
new file mode 100644
index 0000000..0231512
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/jffs2.h
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@infradead.org>
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ *
+ */
+
+#ifndef __LINUX_JFFS2_H__
+#define __LINUX_JFFS2_H__
+
+#define JFFS2_SUPER_MAGIC   0x72b6
+
+/* You must include something which defines the C99 uintXX_t types. 
+   We don't do it from here because this file is used in too many
+   different environments. */
+
+/* Values we may expect to find in the 'magic' field */
+#define JFFS2_OLD_MAGIC_BITMASK 0x1984
+#define JFFS2_MAGIC_BITMASK 0x1985
+#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
+#define JFFS2_EMPTY_BITMASK 0xffff
+#define JFFS2_DIRTY_BITMASK 0x0000
+
+/* Summary node MAGIC marker */
+#define JFFS2_SUM_MAGIC	0x02851885
+
+/* We only allow a single char for length, and 0xFF is empty flash so
+   we don't want it confused with a real length. Hence max 254.
+*/
+#define JFFS2_MAX_NAME_LEN 254
+
+/* How small can we sensibly write nodes? */
+#define JFFS2_MIN_DATA_LEN 128
+
+#define JFFS2_COMPR_NONE	0x00
+#define JFFS2_COMPR_ZERO	0x01
+#define JFFS2_COMPR_RTIME	0x02
+#define JFFS2_COMPR_RUBINMIPS	0x03
+#define JFFS2_COMPR_COPY	0x04
+#define JFFS2_COMPR_DYNRUBIN	0x05
+#define JFFS2_COMPR_ZLIB	0x06
+/* Compatibility flags. */
+#define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
+#define JFFS2_NODE_ACCURATE 0x2000
+/* INCOMPAT: Fail to mount the filesystem */
+#define JFFS2_FEATURE_INCOMPAT 0xc000
+/* ROCOMPAT: Mount read-only */
+#define JFFS2_FEATURE_ROCOMPAT 0x8000
+/* RWCOMPAT_COPY: Mount read/write, and copy the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_COPY 0x4000
+/* RWCOMPAT_DELETE: Mount read/write, and delete the node when it's GC'd */
+#define JFFS2_FEATURE_RWCOMPAT_DELETE 0x0000
+
+#define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
+#define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
+#define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
+
+#define JFFS2_NODETYPE_SUMMARY (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 6)
+
+#define JFFS2_NODETYPE_XATTR (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 8)
+#define JFFS2_NODETYPE_XREF (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 9)
+
+/* XATTR Related */
+#define JFFS2_XPREFIX_USER		1	/* for "user." */
+#define JFFS2_XPREFIX_SECURITY		2	/* for "security." */
+#define JFFS2_XPREFIX_ACL_ACCESS	3	/* for "system.posix_acl_access" */
+#define JFFS2_XPREFIX_ACL_DEFAULT	4	/* for "system.posix_acl_default" */
+#define JFFS2_XPREFIX_TRUSTED		5	/* for "trusted.*" */
+
+#define JFFS2_ACL_VERSION		0x0001
+
+// Maybe later...
+//#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+//#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
+
+
+#define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at
+					   mount time, don't wait for it to
+					   happen later */
+#define JFFS2_INO_FLAG_USERCOMPR  2	/* User has requested a specific
+					   compression type */
+
+
+/* These can go once we've made sure we've caught all uses without
+   byteswapping */
+
+typedef	uint32_t jint32_t;
+
+typedef uint32_t jmode_t;
+
+typedef uint16_t jint16_t;
+
+struct jffs2_unknown_node
+{
+	/* All start like this */
+	jint16_t magic;
+	jint16_t nodetype;
+	jint32_t totlen; /* So we can skip over nodes we don't grok */
+	jint32_t hdr_crc;
+};
+
+struct jffs2_raw_dirent
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS2_NODETYPE_DIRENT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t pino;
+	jint32_t version;
+	jint32_t ino; /* == zero for unlink */
+	jint32_t mctime;
+	uint8_t nsize;
+	uint8_t type;
+	uint8_t unused[2];
+	jint32_t node_crc;
+	jint32_t name_crc;
+	uint8_t name[0];
+};
+
+/* The JFFS2 raw inode structure: Used for storage on physical media.  */
+/* The uid, gid, atime, mtime and ctime members could be longer, but
+   are left like this for space efficiency. If and when people decide
+   they really need them extended, it's simple enough to add support for
+   a new type of raw node.
+*/
+struct jffs2_raw_inode
+{
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS2_NODETYPE_INODE */
+	jint32_t totlen;     /* Total length of this node (inc data, etc.) */
+	jint32_t hdr_crc;
+	jint32_t ino;        /* Inode number.  */
+	jint32_t version;    /* Version number.  */
+	jmode_t mode;       /* The file's type or mode.  */
+	jint16_t uid;        /* The file's owner.  */
+	jint16_t gid;        /* The file's group.  */
+	jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
+	jint32_t atime;      /* Last access time.  */
+	jint32_t mtime;      /* Last modification time.  */
+	jint32_t ctime;      /* Change time.  */
+	jint32_t offset;     /* Where to begin to write.  */
+	jint32_t csize;      /* (Compressed) data size */
+	jint32_t dsize;	     /* Size of the node's data. (after decompression) */
+	uint8_t compr;       /* Compression algorithm used */
+	uint8_t usercompr;   /* Compression algorithm requested by the user */
+	jint16_t flags;	     /* See JFFS2_INO_FLAG_* */
+	jint32_t data_crc;   /* CRC for the (compressed) data.  */
+	jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
+	uint8_t data[0];
+};
+
+struct jffs2_raw_xattr {
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XATTR */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t version;
+	uint8_t xprefix;
+	uint8_t name_len;
+	jint16_t value_len;
+	jint32_t data_crc;
+	jint32_t node_crc;
+	uint8_t data[0];
+} __attribute__((packed));
+
+struct jffs2_raw_xref
+{
+	jint16_t magic;
+	jint16_t nodetype;	/* = JFFS2_NODETYPE_XREF */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t ino;		/* inode number */
+	jint32_t xid;		/* XATTR identifier number */
+	jint32_t xseqno;	/* xref sequencial number */
+	jint32_t node_crc;
+} __attribute__((packed));
+
+struct jffs2_raw_summary
+{
+	jint16_t magic;
+	jint16_t nodetype; 	/* = JFFS2_NODETYPE_SUMMARY */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t sum_num;	/* number of sum entries*/
+	jint32_t cln_mkr;	/* clean marker size, 0 = no cleanmarker */
+	jint32_t padded;	/* sum of the size of padding nodes */
+	jint32_t sum_crc;	/* summary information crc */
+	jint32_t node_crc; 	/* node crc */
+	jint32_t sum[0]; 	/* inode summary info */
+};
+
+union jffs2_node_union
+{
+	struct jffs2_raw_inode i;
+	struct jffs2_raw_dirent d;
+	struct jffs2_raw_xattr x;
+	struct jffs2_raw_xref r;
+	struct jffs2_raw_summary s;
+	struct jffs2_unknown_node u;
+};
+
+/* Data payload for device nodes. */
+union jffs2_device_node {
+	jint16_t old;
+	jint32_t new;
+};
+
+#endif /* __LINUX_JFFS2_H__ */
diff --git a/recipes-devtools/mtd/files/src/linksys_bootcount.c b/recipes-devtools/mtd/files/src/linksys_bootcount.c
new file mode 100644
index 0000000..e689645
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/linksys_bootcount.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Linksys boot counter reset code for mtd
+ *
+ * Copyright (C) 2013 Jonas Gorski <jogo@openwrt.org>
+ * Portions Copyright (c) 2019, Jeff Kletsky
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <endian.h>
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <syslog.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+
+#include "mtd.h"
+
+#define BOOTCOUNT_MAGIC	0x20110811
+
+/*
+ * EA6350v3, and potentially other NOR-boot devices,
+ * use an offset increment of 16 between records,
+ * not mtd_info_user.writesize (often 1 on NOR devices).
+ */
+
+#define BC_OFFSET_INCREMENT_MIN 16
+
+
+
+#define DLOG_OPEN()
+
+#define DLOG_ERR(...) do {						       \
+		fprintf(stderr, "ERROR: " __VA_ARGS__); fprintf(stderr, "\n"); \
+	} while (0)
+
+#define DLOG_NOTICE(...) do {						\
+		fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n");	\
+	} while (0)
+
+#define DLOG_DEBUG(...)
+
+
+
+struct bootcounter {
+	uint32_t magic;
+	uint32_t count;
+	uint32_t checksum;
+};
+
+static char page[2048];
+
+int mtd_resetbc(const char *mtd)
+{
+	struct mtd_info_user mtd_info;
+	struct bootcounter *curr = (struct bootcounter *)page;
+	unsigned int i;
+	unsigned int bc_offset_increment;
+	int last_count = 0;
+	int num_bc;
+	int fd;
+	int ret;
+	int retval = 0;
+
+	DLOG_OPEN();
+
+	fd = mtd_check_open(mtd);
+
+	if (ioctl(fd, MEMGETINFO, &mtd_info) < 0) {
+		DLOG_ERR("Unable to obtain mtd_info for given partition name.");
+
+		retval = -1;
+		goto out;
+	}
+
+
+	/* Detect need to override increment (for EA6350v3) */
+
+	if (mtd_info.writesize < BC_OFFSET_INCREMENT_MIN) {
+
+		bc_offset_increment = BC_OFFSET_INCREMENT_MIN;
+		DLOG_DEBUG("Offset increment set to %i for writesize of %i",
+			   bc_offset_increment, mtd_info.writesize);
+	} else {
+
+		bc_offset_increment = mtd_info.writesize;
+	}
+
+	num_bc = mtd_info.size / bc_offset_increment;
+
+	for (i = 0; i < num_bc; i++) {
+		pread(fd, curr, sizeof(*curr), i * bc_offset_increment);
+
+		/* Existing code assumes erase is to 0xff; left as-is (2019) */
+
+		if (curr->magic != BOOTCOUNT_MAGIC &&
+		    curr->magic != 0xffffffff) {
+			DLOG_ERR("Unexpected magic %08x at offset %08x; aborting.",
+				 curr->magic, i * bc_offset_increment);
+
+			retval = -2;
+			goto out;
+		}
+
+		if (curr->magic == 0xffffffff)
+			break;
+
+		last_count = curr->count;
+	}
+
+
+	if (last_count == 0) {	/* bootcount is already 0 */
+
+		retval = 0;
+		goto out;
+	}
+
+
+	if (i == num_bc) {
+		DLOG_NOTICE("Boot-count log full with %i entries; erasing (expected occasionally).",
+			    i);
+
+		struct erase_info_user erase_info;
+		erase_info.start = 0;
+		erase_info.length = mtd_info.size;
+
+		ret = ioctl(fd, MEMERASE, &erase_info);
+		if (ret < 0) {
+			DLOG_ERR("Failed to erase boot-count log MTD; ioctl() MEMERASE returned %i",
+				 ret);
+
+			retval = -3;
+			goto out;
+		}
+
+		i = 0;
+	}
+
+	memset(curr, 0xff, bc_offset_increment);
+
+	curr->magic = BOOTCOUNT_MAGIC;
+	curr->count = 0;
+	curr->checksum = BOOTCOUNT_MAGIC;
+
+	/* Assumes bc_offset_increment is a multiple of mtd_info.writesize */
+
+	ret = pwrite(fd, curr, bc_offset_increment, i * bc_offset_increment);
+	if (ret < 0) {
+		DLOG_ERR("Failed to write boot-count log entry; pwrite() returned %i",
+			 errno);
+		retval = -4;
+		goto out;
+
+	} else {
+		sync();
+
+		DLOG_NOTICE("Boot count sucessfully reset to zero.");
+
+		retval = 0;
+		goto out;
+	}
+
+out:
+	close(fd);
+	return retval;
+}
diff --git a/recipes-devtools/mtd/files/src/md5.c b/recipes-devtools/mtd/files/src/md5.c
new file mode 100644
index 0000000..e1c4be4
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/md5.c
@@ -0,0 +1,307 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines                         **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
+ ** Created: 2/17/90 RLR                                              **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
+ **                                                                   **
+ ** License to copy and use this software is granted provided that    **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
+ ** Digest Algorithm" in all material mentioning or referencing this  **
+ ** software or this function.                                        **
+ **                                                                   **
+ ** License is also granted to make and use derivative works          **
+ ** provided that such works are identified as "derived from the RSA  **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
+ ** material mentioning or referencing the derived work.              **
+ **                                                                   **
+ ** RSA Data Security, Inc. makes no representations concerning       **
+ ** either the merchantability of this software or the suitability    **
+ ** of this software for any particular purpose.  It is provided "as  **
+ ** is" without express or implied warranty of any kind.              **
+ **                                                                   **
+ ** These notices must be retained in any copies of any part of this  **
+ ** documentation and/or software.                                    **
+ ***********************************************************************
+ */
+
+#include <string.h>
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ **  Message-digest routines:                                         **
+ **  To form the message digest for a message M                       **
+ **    (1) Initialize a context buffer mdContext using MD5_Init       **
+ **    (2) Call MD5_Update on mdContext and M                         **
+ **    (3) Call MD5_Final on mdContext                                **
+ **  The message digest is now in mdContext->digest[0...15]           **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform ();
+
+static unsigned char PADDING[64] = {
+  0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+  {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define GG(a, b, c, d, x, s, ac) \
+  {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define HH(a, b, c, d, x, s, ac) \
+  {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+#define II(a, b, c, d, x, s, ac) \
+  {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+   (a) = ROTATE_LEFT ((a), (s)); \
+   (a) += (b); \
+  }
+
+#ifdef __STDC__
+#define UL(x)	x##U
+#else
+#define UL(x)	x
+#endif
+
+/* The routine MD5_Init initializes the message-digest context
+   mdContext. All fields are set to zero.
+ */
+void MD5_Init (mdContext)
+MD5_CTX *mdContext;
+{
+  mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+  /* Load magic initialization constants.
+   */
+  mdContext->buf[0] = (UINT4)0x67452301;
+  mdContext->buf[1] = (UINT4)0xefcdab89;
+  mdContext->buf[2] = (UINT4)0x98badcfe;
+  mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+   account for the presence of each of the characters inBuf[0..inLen-1]
+   in the message whose digest is being computed.
+ */
+void MD5_Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* update number of bits */
+  if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+    mdContext->i[1]++;
+  mdContext->i[0] += ((UINT4)inLen << 3);
+  mdContext->i[1] += ((UINT4)inLen >> 29);
+
+  while (inLen--) {
+    /* add new character to buffer, increment mdi */
+    mdContext->in[mdi++] = *inBuf++;
+
+    /* transform if necessary */
+    if (mdi == 0x40) {
+      for (i = 0, ii = 0; i < 16; i++, ii += 4)
+        in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+                (((UINT4)mdContext->in[ii+2]) << 16) |
+                (((UINT4)mdContext->in[ii+1]) << 8) |
+                ((UINT4)mdContext->in[ii]);
+      Transform (mdContext->buf, in);
+      mdi = 0;
+    }
+  }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+   ends with the desired message digest in mdContext->digest[0...15].
+ */
+void MD5_Final (hash, mdContext)
+unsigned char hash[];
+MD5_CTX *mdContext;
+{
+  UINT4 in[16];
+  int mdi;
+  unsigned int i, ii;
+  unsigned int padLen;
+
+  /* save number of bits */
+  in[14] = mdContext->i[0];
+  in[15] = mdContext->i[1];
+
+  /* compute number of bytes mod 64 */
+  mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+  /* pad out to 56 mod 64 */
+  padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+  MD5_Update (mdContext, PADDING, padLen);
+
+  /* append length in bits and transform */
+  for (i = 0, ii = 0; i < 14; i++, ii += 4)
+    in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+            (((UINT4)mdContext->in[ii+2]) << 16) |
+            (((UINT4)mdContext->in[ii+1]) << 8) |
+            ((UINT4)mdContext->in[ii]);
+  Transform (mdContext->buf, in);
+
+  /* store buffer in digest */
+  for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+    mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+    mdContext->digest[ii+1] =
+      (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+    mdContext->digest[ii+2] =
+      (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+    mdContext->digest[ii+3] =
+      (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+  }
+  memcpy(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+  UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+  /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+  FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+  FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+  FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+  FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+  FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+  FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+  FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+  FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+  FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+  FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+  FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+  FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+  FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+  FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+  FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+  FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+  /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+  GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+  GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+  GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+  GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+  GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+  GG ( d, a, b, c, in[10], S22, UL(  38016083)); /* 22 */
+  GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+  GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+  GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+  GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+  GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+  GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+  GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+  GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+  GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+  GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+  /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+  HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+  HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+  HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+  HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+  HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+  HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+  HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+  HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+  HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+  HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+  HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+  HH ( b, c, d, a, in[ 6], S34, UL(  76029189)); /* 44 */
+  HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+  HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+  HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+  HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+  /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+  II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+  II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+  II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+  II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+  II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+  II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+  II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+  II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+  II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+  II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+  II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+  II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+  II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+  II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+  II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+  II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+  buf[0] += a;
+  buf[1] += b;
+  buf[2] += c;
+  buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c                                                      **
+ ******************************** (cut) ********************************
+ */
diff --git a/recipes-devtools/mtd/files/src/md5.h b/recipes-devtools/mtd/files/src/md5.h
new file mode 100644
index 0000000..f63d74e
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/md5.h
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5                    **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm              **
+ ** Created: 2/17/90 RLR                                              **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version               **
+ ** Revised (for MD5): RLR 4/27/91                                    **
+ **   -- G modified to have y&~z instead of y&z                       **
+ **   -- FF, GG, HH modified to add in last register done             **
+ **   -- Access pattern: round 2 works mod 5, round 3 works mod 3     **
+ **   -- distinct additive constant for each step                     **
+ **   -- round 4 added, working mod 7                                 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved.  **
+ **                                                                   **
+ ** License to copy and use this software is granted provided that    **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message-     **
+ ** Digest Algorithm" in all material mentioning or referencing this  **
+ ** software or this function.                                        **
+ **                                                                   **
+ ** License is also granted to make and use derivative works          **
+ ** provided that such works are identified as "derived from the RSA  **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all          **
+ ** material mentioning or referencing the derived work.              **
+ **                                                                   **
+ ** RSA Data Security, Inc. makes no representations concerning       **
+ ** either the merchantability of this software or the suitability    **
+ ** of this software for any particular purpose.  It is provided "as  **
+ ** is" without express or implied warranty of any kind.              **
+ **                                                                   **
+ ** These notices must be retained in any copies of any part of this  **
+ ** documentation and/or software.                                    **
+ ***********************************************************************
+ */
+
+#ifndef __MD5_INCLUDE__
+
+/* typedef a 32-bit type */
+#ifdef _LP64
+typedef unsigned int UINT4;
+typedef int          INT4;
+#else
+typedef unsigned long UINT4;
+typedef long          INT4;
+#endif
+#define _UINT4_T
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+  UINT4 i[2];                   /* number of _bits_ handled mod 2^64 */
+  UINT4 buf[4];                                    /* scratch buffer */
+  unsigned char in[64];                              /* input buffer */
+  unsigned char digest[16];     /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5_Init ();
+void MD5_Update ();
+void MD5_Final ();
+
+#define __MD5_INCLUDE__
+#endif /* __MD5_INCLUDE__ */
diff --git a/recipes-devtools/mtd/files/src/mtd.c b/recipes-devtools/mtd/files/src/mtd.c
new file mode 100644
index 0000000..1b46cf4
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/mtd.c
@@ -0,0 +1,1196 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * mtd - simple memory technology device manipulation tool
+ *
+ * Copyright (C) 2005      Waldemar Brodkorb <wbx@dass-it.de>,
+ * Copyright (C) 2005-2009 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *
+ * The code is based on the linux-mtd examples.
+ */
+
+#define _GNU_SOURCE
+#include <byteswap.h>
+#include <endian.h>
+#include <limits.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/reboot.h>
+#include <linux/reboot.h>
+#include <mtd/mtd-user.h>
+#include "crc32.h"
+#include "fis.h"
+#include "mtd.h"
+
+#include <libubox/md5.h>
+
+#define MAX_ARGS 8
+#define JFFS2_DEFAULT_DIR	"" /* directory name without /, empty means root dir */
+
+#define TRX_MAGIC		0x48445230	/* "HDR0" */
+#define SEAMA_MAGIC		0x5ea3a417
+#define WRG_MAGIC		0x20040220
+#define WRGG03_MAGIC		0x20080321
+
+#if !defined(__BYTE_ORDER)
+#error "Unknown byte order"
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_be32(x)	(x)
+#define be32_to_cpu(x)	(x)
+#define le32_to_cpu(x)	bswap_32(x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_be32(x)	bswap_32(x)
+#define be32_to_cpu(x)	bswap_32(x)
+#define le32_to_cpu(x)  (x)
+#else
+#error "Unsupported endianness"
+#endif
+
+enum mtd_image_format {
+	MTD_IMAGE_FORMAT_UNKNOWN,
+	MTD_IMAGE_FORMAT_TRX,
+	MTD_IMAGE_FORMAT_SEAMA,
+	MTD_IMAGE_FORMAT_WRG,
+	MTD_IMAGE_FORMAT_WRGG03,
+};
+
+static char *buf = NULL;
+static char *imagefile = NULL;
+static enum mtd_image_format imageformat = MTD_IMAGE_FORMAT_UNKNOWN;
+static char *jffs2file = NULL, *jffs2dir = JFFS2_DEFAULT_DIR;
+static char *tpl_uboot_args_part;
+static int buflen = 0;
+int quiet;
+int no_erase;
+int mtdsize = 0;
+int erasesize = 0;
+int writesize = 0;
+int jffs2_skip_bytes=0;
+int mtdtype = 0;
+
+int mtd_open(const char *mtd, bool block)
+{
+	FILE *fp;
+	char dev[PATH_MAX];
+	int i;
+	int ret;
+	int flags = O_RDWR | O_SYNC;
+	char name[PATH_MAX];
+
+	snprintf(name, sizeof(name), "\"%s\"", mtd);
+	if ((fp = fopen("/proc/mtd", "r"))) {
+		while (fgets(dev, sizeof(dev), fp)) {
+			if (sscanf(dev, "mtd%d:", &i) && strstr(dev, name)) {
+				snprintf(dev, sizeof(dev), "/dev/mtd%s/%d", (block ? "block" : ""), i);
+				if ((ret=open(dev, flags))<0) {
+					snprintf(dev, sizeof(dev), "/dev/mtd%s%d", (block ? "block" : ""), i);
+					ret=open(dev, flags);
+				}
+				fclose(fp);
+				return ret;
+			}
+		}
+		fclose(fp);
+	}
+
+	return open(mtd, flags);
+}
+
+int mtd_check_open(const char *mtd)
+{
+	struct mtd_info_user mtdInfo;
+	int fd;
+
+	fd = mtd_open(mtd, false);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		return -1;
+	}
+
+	if(ioctl(fd, MEMGETINFO, &mtdInfo)) {
+		fprintf(stderr, "Could not get MTD device info from %s\n", mtd);
+		close(fd);
+		return -1;
+	}
+	mtdsize = mtdInfo.size;
+	erasesize = mtdInfo.erasesize;
+	writesize = mtdInfo.writesize;
+	mtdtype = mtdInfo.type;
+
+	return fd;
+}
+
+int mtd_block_is_bad(int fd, int offset)
+{
+	int r = 0;
+	loff_t o = offset;
+
+	if (mtdtype == MTD_NANDFLASH)
+	{
+		r = ioctl(fd, MEMGETBADBLOCK, &o);
+		if (r < 0)
+		{
+			fprintf(stderr, "Failed to get erase block status at 0x%x errno:%d\n", offset, errno);
+			exit(1);
+		}
+	}
+	return r;
+}
+
+int mtd_block_set_bad(int fd, int offset)
+{
+	int r = 0;
+	loff_t o = offset;
+
+	if (mtdtype == MTD_NANDFLASH)
+	{
+		r = ioctl(fd, MEMSETBADBLOCK, &o);
+		if (r < 0)
+			fprintf(stderr, "Failed to set erase block status at 0x%x errno:%d\n", offset, errno);
+	}
+	return r;
+}
+
+int mtd_erase_block(int fd, int offset)
+{
+	struct erase_info_user mtdEraseInfo;
+
+	mtdEraseInfo.start = offset;
+	mtdEraseInfo.length = erasesize;
+	ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
+	if (ioctl (fd, MEMERASE, &mtdEraseInfo) < 0)
+		return -1;
+
+	return 0;
+}
+
+int mtd_write_buffer(int fd, const char *buf, int offset, int length)
+{
+	lseek(fd, offset, SEEK_SET);
+	write(fd, buf, length);
+	return 0;
+}
+
+static int
+image_check(int imagefd, const char *mtd)
+{
+	uint32_t magic;
+	int ret = 1;
+	int bufread;
+
+	while (buflen < sizeof(magic)) {
+		bufread = read(imagefd, buf + buflen, sizeof(magic) - buflen);
+		if (bufread < 1)
+			break;
+
+		buflen += bufread;
+	}
+
+	if (buflen < sizeof(magic)) {
+		fprintf(stdout, "Could not get image magic\n");
+		return 0;
+	}
+
+	magic = ((uint32_t *)buf)[0];
+
+	if (be32_to_cpu(magic) == TRX_MAGIC)
+		imageformat = MTD_IMAGE_FORMAT_TRX;
+	else if (be32_to_cpu(magic) == SEAMA_MAGIC)
+		imageformat = MTD_IMAGE_FORMAT_SEAMA;
+	else if (le32_to_cpu(magic) == WRG_MAGIC)
+		imageformat = MTD_IMAGE_FORMAT_WRG;
+	else if (le32_to_cpu(magic) == WRGG03_MAGIC)
+		imageformat = MTD_IMAGE_FORMAT_WRGG03;
+
+	switch (imageformat) {
+	case MTD_IMAGE_FORMAT_TRX:
+		if (trx_check)
+			ret = trx_check(imagefd, mtd, buf, &buflen);
+		break;
+	case MTD_IMAGE_FORMAT_SEAMA:
+	case MTD_IMAGE_FORMAT_WRG:
+	case MTD_IMAGE_FORMAT_WRGG03:
+		break;
+	default:
+#ifdef target_brcm
+		if (!strcmp(mtd, "firmware"))
+			ret = 0;
+#endif
+		break;
+	}
+
+	return ret;
+}
+
+static int mtd_check(const char *mtd)
+{
+	char *next = NULL;
+	char *str = NULL;
+	int fd;
+
+	if (strchr(mtd, ':')) {
+		str = strdup(mtd);
+		mtd = str;
+	}
+
+	do {
+		next = strchr(mtd, ':');
+		if (next) {
+			*next = 0;
+			next++;
+		}
+
+		fd = mtd_check_open(mtd);
+		if (fd < 0)
+			return 0;
+
+		if (!buf)
+			buf = malloc(erasesize);
+
+		close(fd);
+		mtd = next;
+	} while (next);
+
+	if (str)
+		free(str);
+
+	return 1;
+}
+
+static int
+mtd_unlock(const char *mtd)
+{
+	struct erase_info_user mtdLockInfo;
+	char *next = NULL;
+	char *str = NULL;
+	int fd;
+
+	if (strchr(mtd, ':')) {
+		str = strdup(mtd);
+		mtd = str;
+	}
+
+	do {
+		next = strchr(mtd, ':');
+		if (next) {
+			*next = 0;
+			next++;
+		}
+
+		fd = mtd_check_open(mtd);
+		if(fd < 0) {
+			fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+			exit(1);
+		}
+
+		if (quiet < 2)
+			fprintf(stderr, "Unlocking %s ...\n", mtd);
+
+		mtdLockInfo.start = 0;
+		mtdLockInfo.length = mtdsize;
+		ioctl(fd, MEMUNLOCK, &mtdLockInfo);
+		close(fd);
+		mtd = next;
+	} while (next);
+
+	if (str)
+		free(str);
+
+	return 0;
+}
+
+static int
+mtd_erase(const char *mtd)
+{
+	int fd;
+	struct erase_info_user mtdEraseInfo;
+
+	if (quiet < 2)
+		fprintf(stderr, "Erasing %s ...\n", mtd);
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	mtdEraseInfo.length = erasesize;
+
+	for (mtdEraseInfo.start = 0;
+		 mtdEraseInfo.start < mtdsize;
+		 mtdEraseInfo.start += erasesize) {
+		if (mtd_block_is_bad(fd, mtdEraseInfo.start)) {
+			if (!quiet)
+				fprintf(stderr, "\nSkipping bad block at 0x%x\n", mtdEraseInfo.start);
+		} else {
+			ioctl(fd, MEMUNLOCK, &mtdEraseInfo);
+			if(ioctl(fd, MEMERASE, &mtdEraseInfo)) {
+				fprintf(stderr, "Failed to erase block on %s at 0x%x errno: %d\n", mtd, mtdEraseInfo.start, errno);
+				if (errno == EIO)
+					if(mtd_block_set_bad(fd, mtdEraseInfo.start) < 0)
+						fprintf(stderr, "Failed to mark bad block on %s at 0x%x\n", mtd, mtdEraseInfo.start);
+			}
+		}
+	}
+
+	close(fd);
+	return 0;
+
+}
+
+static int
+mtd_dump(const char *mtd, int part_offset, int size)
+{
+	int ret = 0, offset = 0;
+	int fd;
+	char *buf;
+
+	if (quiet < 2)
+		fprintf(stderr, "Dumping %s ...\n", mtd);
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		return -1;
+	}
+
+	if (!size)
+		size = mtdsize;
+
+	if (part_offset)
+		lseek(fd, part_offset, SEEK_SET);
+
+	buf = malloc(erasesize);
+	if (!buf)
+		return -1;
+
+	do {
+		int len = (size > erasesize) ? (erasesize) : (size);
+		int rlen = read(fd, buf, len);
+
+		if (rlen < 0) {
+			if (errno == EINTR)
+				continue;
+			ret = -1;
+			goto out;
+		}
+		if (!rlen || rlen != len)
+			break;
+		if (mtd_block_is_bad(fd, offset)) {
+			fprintf(stderr, "skipping bad block at 0x%08x\n", offset);
+		} else {
+			size -= rlen;
+			write(1, buf, rlen);
+		}
+		offset += rlen;
+	} while (size > 0);
+
+out:
+	close(fd);
+	return ret;
+}
+
+static int
+mtd_verify(const char *mtd, char *file)
+{
+#if 1
+	uint32_t f_md5[4], m_md5[4];
+	struct stat s;
+	md5_ctx_t ctx;
+	int ret = 0;
+	int fd;
+
+	if (quiet < 2)
+		fprintf(stderr, "Verifying %s against %s ...\n", mtd, file);
+
+	if (stat(file, &s) || md5sum(file, f_md5) < 0) {
+		fprintf(stderr, "Failed to hash %s\n", file);
+		return -1;
+	}
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		return -1;
+	}
+
+	md5_begin(&ctx);
+	do {
+		char buf[256];
+		int len = (s.st_size > sizeof(buf)) ? (sizeof(buf)) : (s.st_size);
+		int rlen = read(fd, buf, len);
+
+		if (rlen < 0) {
+			if (errno == EINTR)
+				continue;
+			ret = -1;
+			goto out;
+		}
+		if (!rlen)
+			break;
+		md5_hash(buf, rlen, &ctx);
+		s.st_size -= rlen;
+	} while (s.st_size > 0);
+
+	md5_end(m_md5, &ctx);
+
+	fprintf(stderr, "%08x%08x%08x%08x - %s\n", m_md5[0], m_md5[1], m_md5[2], m_md5[3], mtd);
+	fprintf(stderr, "%08x%08x%08x%08x - %s\n", f_md5[0], f_md5[1], f_md5[2], f_md5[3], file);
+
+	ret = memcmp(f_md5, m_md5, sizeof(m_md5));
+	if (!ret)
+		fprintf(stderr, "Success\n");
+	else
+		fprintf(stderr, "Failed\n");
+
+out:
+	close(fd);
+#endif
+	return 0;
+}
+
+static void
+indicate_writing(const char *mtd)
+{
+	if (quiet < 2)
+		fprintf(stderr, "\nWriting from %s to %s ... ", imagefile, mtd);
+
+	if (!quiet)
+		fprintf(stderr, " [ ]");
+}
+
+static int
+mtd_write(int imagefd, const char *mtd, char *fis_layout, size_t part_offset)
+{
+	char *next = NULL;
+	char *str = NULL;
+	int fd, result;
+	ssize_t r, w, e;
+	ssize_t skip = 0;
+	uint32_t offset = 0;
+	int buflen_raw = 0;
+	uint32_t cur_off = 0,phy_part_off = 0;
+	int jffs2_replaced = 0;
+	int skip_bad_blocks = 0;
+
+#ifdef FIS_SUPPORT
+	static struct fis_part new_parts[MAX_ARGS];
+	static struct fis_part old_parts[MAX_ARGS];
+	struct fis_part *cur_part = NULL;
+	int n_new = 0, n_old = 0;
+
+	if (fis_layout) {
+		const char *tmp = mtd;
+		char *word, *brkt;
+		int ret;
+
+		memset(&old_parts, 0, sizeof(old_parts));
+		memset(&new_parts, 0, sizeof(new_parts));
+		if (!part_offset)
+			cur_part = new_parts;
+
+		do {
+			next = strchr(tmp, ':');
+			if (!next)
+				next = (char *) tmp + strlen(tmp);
+
+			memcpy(old_parts[n_old].name, tmp, next - tmp);
+
+			n_old++;
+			tmp = next + 1;
+		} while(*next);
+
+		for (word = strtok_r(fis_layout, ",", &brkt);
+		     word;
+			 word = strtok_r(NULL, ",", &brkt)) {
+
+			tmp = strtok(word, ":");
+			strncpy((char *) new_parts[n_new].name, tmp, sizeof(new_parts[n_new].name) - 1);
+
+			tmp = strtok(NULL, ":");
+			if (!tmp)
+				goto next;
+
+			new_parts[n_new].size = strtoul(tmp, NULL, 0);
+
+			tmp = strtok(NULL, ":");
+			if (!tmp)
+				goto next;
+
+			new_parts[n_new].loadaddr = strtoul(tmp, NULL, 16);
+next:
+			n_new++;
+		}
+		ret = fis_validate(old_parts, n_old, new_parts, n_new);
+		if (ret < 0) {
+			fprintf(stderr, "Failed to validate the new FIS partition table\n");
+			exit(1);
+		}
+		if (ret == 0)
+			fis_layout = NULL;
+	}
+#endif
+
+	if (strchr(mtd, ':')) {
+		str = strdup(mtd);
+		mtd = str;
+	}
+
+	r = 0;
+
+resume:
+	next = strchr(mtd, ':');
+	if (next) {
+		*next = 0;
+		next++;
+	}
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+	if (part_offset > 0) {
+		if (part_offset % erasesize != 0) {
+			fprintf(stderr, "Seeking on mtd device '%s' to: %zu, should align erase size \n", mtd, part_offset);
+			exit(1);
+		}
+
+		for (; phy_part_off < mtdsize;) {
+			if (cur_off == part_offset)
+				break;
+
+			if (!mtd_block_is_bad(fd, phy_part_off))
+				cur_off += erasesize;
+			else {
+				if (!quiet)
+					fprintf(stderr, "\nSkipping bad block at 0x%08zx\n", phy_part_off);
+
+				skip_bad_blocks += erasesize;
+			}
+
+			phy_part_off += erasesize;
+		}
+
+		if (phy_part_off == mtdsize) {
+			fprintf(stderr, "Out of range on seeking on mtd device '%s' to: %zu\n", mtd, part_offset);
+			exit(1);
+		}
+
+		fprintf(stderr, "Seeking on mtd device '%s' to: %zu\n", mtd, part_offset);
+		lseek(fd, phy_part_off, SEEK_SET);
+	}
+
+	/* Write TP-Link recovery flag */
+	if (tpl_uboot_args_part && mtd_tpl_recoverflag_write) {
+		if (quiet < 2)
+			fprintf(stderr, "Writing recovery flag to %s\n", tpl_uboot_args_part);
+		result = mtd_tpl_recoverflag_write(tpl_uboot_args_part, true);
+		if (result < 0) {
+			fprintf(stderr, "Could not write TP-Link recovery flag to %s: %i", mtd, result);
+			exit(1);
+		}
+	}
+
+	indicate_writing(mtd);
+
+	w = e = 0;
+	for (;;) {
+		/* buffer may contain data already (from trx check or last mtd partition write attempt) */
+		while (buflen < erasesize) {
+			r = read(imagefd, buf + buflen, erasesize - buflen);
+			if (r < 0) {
+				if ((errno == EINTR) || (errno == EAGAIN))
+					continue;
+				else {
+					perror("read");
+					break;
+				}
+			}
+
+			if (r == 0)
+				break;
+
+			buflen += r;
+		}
+
+		if (buflen_raw == 0)
+			buflen_raw = buflen;
+
+		if (buflen == 0)
+			break;
+
+		if (buflen < erasesize) {
+			int align_page = ((buflen + writesize - 1) / writesize) * writesize;
+			/* Pad buf to page size */
+			if (align_page > buflen)
+				memset(&buf[buflen], 0xff, align_page - buflen);
+
+			buflen = align_page;
+		}
+
+		if (skip > 0) {
+			skip -= buflen;
+			buflen_raw = 0;
+			buflen = 0;
+			if (skip <= 0)
+				indicate_writing(mtd);
+
+			continue;
+		}
+
+		if (jffs2file && w >= jffs2_skip_bytes) {
+			if (memcmp(buf, JFFS2_EOF, sizeof(JFFS2_EOF) - 1) == 0) {
+				if (!quiet)
+					fprintf(stderr, "\b\b\b   ");
+				if (quiet < 2)
+					fprintf(stderr, "\nAppending jffs2 data from %s to %s..\n.", jffs2file, mtd);
+				/* got an EOF marker - this is the place to add some jffs2 data */
+				skip = mtd_replace_jffs2(mtd, fd, e, jffs2file);
+				jffs2_replaced = 1;
+
+				/* don't add it again */
+				jffs2file = NULL;
+
+				w += skip;
+				e += skip;
+				skip -= buflen;
+				buflen_raw = 0;
+				buflen = 0;
+				offset = 0;
+				continue;
+			}
+			/* no EOF marker, make sure we figure out the last inode number
+			 * before appending some data */
+			mtd_parse_jffs2data(buf, jffs2dir);
+		}
+
+rewrite:
+		/* need to erase the next block before writing data to it */
+		if(!no_erase)
+		{
+			while (w + buflen > e - skip_bad_blocks) {
+				if (!quiet)
+					fprintf(stderr, "\b\b\b[e]");
+
+				if (mtd_block_is_bad(fd, e + phy_part_off)) {
+					if (!quiet)
+						fprintf(stderr, "\nSkipping bad block at 0x%08zx\n", e + phy_part_off);
+
+					skip_bad_blocks += erasesize;
+					e += erasesize;
+
+					// Move the file pointer along over the bad block.
+					lseek(fd, erasesize, SEEK_CUR);
+					continue;
+				}
+
+				if (mtd_erase_block(fd, e + phy_part_off) < 0) {
+					if (errno != EIO) {
+						fprintf(stderr, "Failed to erase block, unhandled errno: %d off: %zu\n", errno, e + phy_part_off);
+						exit(1);
+					}
+
+					if(mtd_block_set_bad(fd, e + phy_part_off) < 0){
+						fprintf(stderr, "Failed to mark block, errno: %d off: %zu\n", errno, e + phy_part_off);
+						exit(1);
+					}
+
+					if ((e + phy_part_off + erasesize) <= mtdsize) {
+						if (!quiet)
+							fprintf(stderr, "\nSkipping worn bad block at 0x%08zx\n", e + phy_part_off);
+
+						skip_bad_blocks += erasesize;
+						e += erasesize;
+
+						// Move the file pointer along over the bad block.
+						lseek(fd, erasesize, SEEK_CUR);
+
+						continue;
+					} else if (next) {
+						if (w < e) {
+							write(fd, buf + offset, e - w);
+							offset = e - w;
+						}
+						w = 0;
+						e = 0;
+						phy_part_off = 0;
+						close(fd);
+						mtd = next;
+						fprintf(stderr, "\b\b\b   \n");
+						goto resume;
+					} else {
+						fprintf(stderr, "Failed to erase block\n");
+						exit(1);
+					}
+				}
+
+				/* erase the chunk */
+				e += erasesize;
+
+				if (e + phy_part_off > mtdsize) {
+					fprintf(stderr, "Insufficient space.\n");
+					exit(1);
+				}
+			}
+		}
+
+		if (!quiet)
+			fprintf(stderr, "\b\b\b[w]");
+
+		if ((result = write(fd, buf + offset, buflen)) < buflen) {
+			if (result < 0) {
+				if (errno == EIO) {
+					if(mtd_block_set_bad(fd, e - erasesize + phy_part_off) < 0){
+						fprintf(stderr, "Failed to mark block, errno: %d off: %zu\n", errno, e - erasesize + phy_part_off);
+						exit(1);
+					}
+
+					if (!quiet)
+						fprintf(stderr, "\nSkipping worn bad block at 0x%08zx\n", e - erasesize + phy_part_off);
+
+					skip_bad_blocks += erasesize;
+					lseek(fd, e + phy_part_off, SEEK_SET);
+
+					goto rewrite;
+				} else {
+					fprintf(stderr, "Error writing image.\n");
+					exit(1);
+				}
+			} else {
+				fprintf(stderr, "Insufficient space.\n");
+				exit(1);
+			}
+		}
+		w += buflen;
+
+#ifdef FIS_SUPPORT
+		if (cur_part && cur_part->size
+		&& cur_part < &new_parts[MAX_ARGS - 1]
+		&& cur_part->length + buflen_raw > cur_part->size)
+			cur_part++;
+		if (cur_part) {
+			cur_part->length += buflen_raw;
+			cur_part->crc = crc32(cur_part->crc, buf, buflen_raw);
+		}
+#endif
+		buflen_raw = 0;
+		buflen = 0;
+		offset = 0;
+	}
+
+	if (jffs2_replaced) {
+		switch (imageformat) {
+		case MTD_IMAGE_FORMAT_TRX:
+			if (trx_fixup)
+				trx_fixup(fd, mtd);
+			break;
+		case MTD_IMAGE_FORMAT_SEAMA:
+			if (mtd_fixseama)
+				mtd_fixseama(mtd, 0, 0);
+			break;
+		case MTD_IMAGE_FORMAT_WRG:
+			if (mtd_fixwrg)
+				mtd_fixwrg(mtd, 0, 0);
+			break;
+		case MTD_IMAGE_FORMAT_WRGG03:
+			if (mtd_fixwrgg)
+				mtd_fixwrgg(mtd, 0, 0);
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!quiet)
+		fprintf(stderr, "\b\b\b\b    ");
+
+	if (quiet < 2)
+		fprintf(stderr, "\n");
+
+#ifdef FIS_SUPPORT
+	if (fis_layout) {
+		if (fis_remap(old_parts, n_old, new_parts, n_new) < 0)
+			fprintf(stderr, "Failed to update the FIS partition table\n");
+	}
+#endif
+
+	close(fd);
+
+	/* Clear TP-Link recovery flag */
+	if (tpl_uboot_args_part && mtd_tpl_recoverflag_write) {
+		if (quiet < 2)
+			fprintf(stderr, "Removing recovery flag from %s\n", tpl_uboot_args_part);
+		result = mtd_tpl_recoverflag_write(tpl_uboot_args_part, false);
+		if (result < 0) {
+			fprintf(stderr, "Could not clear TP-Link recovery flag to %s: %i", mtd, result);
+			exit(1);
+		}
+	}
+
+	return 0;
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "Usage: mtd [<options> ...] <command> [<arguments> ...] <device>[:<device>...]\n\n"
+	"The device is in the format of mtdX (eg: mtd4) or its label.\n"
+	"mtd recognizes these commands:\n"
+	"        unlock                  unlock the device\n"
+	"        refresh                 refresh mtd partition\n"
+	"        erase                   erase all data on device\n"
+	"        verify <imagefile>|-    verify <imagefile> (use - for stdin) to device\n"
+	"        write <imagefile>|-     write <imagefile> (use - for stdin) to device\n"
+	"        jffs2write <file>       append <file> to the jffs2 partition on the device\n");
+	if (mtd_resetbc) {
+	    fprintf(stderr,
+	"        resetbc <device>        reset the uboot boot counter\n");
+	}
+	if (mtd_fixtrx) {
+	    fprintf(stderr,
+	"        fixtrx                  fix the checksum in a trx header on first boot\n");
+	}
+	if (mtd_fixseama) {
+	    fprintf(stderr,
+	"        fixseama                fix the checksum in a seama header on first boot\n");
+	}
+	if (mtd_fixwrg) {
+	    fprintf(stderr,
+	"        fixwrg                  fix the checksum in a wrg header on first boot\n");
+	}
+	if (mtd_fixwrgg) {
+	    fprintf(stderr,
+	"        fixwrgg                 fix the checksum in a wrgg header on first boot\n");
+	}
+	fprintf(stderr,
+	"Following options are available:\n"
+	"        -q                      quiet mode (once: no [w] on writing,\n"
+	"                                           twice: no status messages)\n"
+	"        -n                      write without first erasing the blocks\n"
+	"        -r                      reboot after successful command\n"
+	"        -f                      force write without trx checks\n"
+	"        -e <device>             erase <device> before executing the command\n"
+	"        -d <name>               directory for jffs2write, defaults to \"tmp\"\n"
+	"        -j <name>               integrate <file> into jffs2 data when writing an image\n"
+	"        -s <number>             skip the first n bytes when appending data to the jffs2 partiton, defaults to \"0\"\n"
+	"        -p <number>             write beginning at partition offset\n"
+	"        -l <length>             the length of data that we want to dump\n");
+	if (mtd_fixtrx) {
+	    fprintf(stderr,
+	"        -o offset               offset of the image header in the partition(for fixtrx)\n");
+	}
+	if (mtd_fixtrx || mtd_fixseama || mtd_fixwrg || mtd_fixwrgg) {
+		fprintf(stderr,
+	"        -c datasize             amount of data to be used for checksum calculation (for fixtrx / fixseama / fixwrg / fixwrgg)\n");
+	}
+	if (mtd_tpl_recoverflag_write) {
+		fprintf(stderr,
+	"        -t <partition>          write TP-Link recovery-flag to <partition> (for write)\n");
+	}
+	fprintf(stderr,
+#ifdef FIS_SUPPORT
+	"        -F <part>[:<size>[:<entrypoint>]][,<part>...]\n"
+	"                                alter the fis partition table to create new partitions replacing\n"
+	"                                the partitions provided as argument to the write command\n"
+	"                                (only valid together with the write command)\n"
+#endif
+	"\n"
+	"Example: To write linux.trx to mtd4 labeled as linux and reboot afterwards\n"
+	"         mtd -r write linux.trx linux\n\n");
+	exit(1);
+}
+
+static void do_reboot(void)
+{
+	fprintf(stderr, "Rebooting ...\n");
+	fflush(stderr);
+
+	/* try regular reboot method first */
+	system("/sbin/reboot");
+	sleep(2);
+
+	/* if we're still alive at this point, force the kernel to reboot */
+	syscall(SYS_reboot,LINUX_REBOOT_MAGIC1,LINUX_REBOOT_MAGIC2,LINUX_REBOOT_CMD_RESTART,NULL);
+}
+
+int main (int argc, char **argv)
+{
+	int ch, i, boot, imagefd = 0, force, unlocked;
+	char *erase[MAX_ARGS], *device = NULL;
+	char *fis_layout = NULL;
+	size_t offset = 0, data_size = 0, part_offset = 0, dump_len = 0;
+	enum {
+		CMD_ERASE,
+		CMD_WRITE,
+		CMD_UNLOCK,
+		CMD_JFFS2WRITE,
+		CMD_FIXTRX,
+		CMD_FIXSEAMA,
+		CMD_FIXWRG,
+		CMD_FIXWRGG,
+		CMD_VERIFY,
+		CMD_DUMP,
+		CMD_RESETBC,
+	} cmd = -1;
+
+	erase[0] = NULL;
+	boot = 0;
+	force = 0;
+	buflen = 0;
+	quiet = 0;
+	no_erase = 0;
+
+	while ((ch = getopt(argc, argv,
+#ifdef FIS_SUPPORT
+			"F:"
+#endif
+			"frnqe:d:s:j:p:o:c:t:l:")) != -1)
+		switch (ch) {
+			case 'f':
+				force = 1;
+				break;
+			case 'r':
+				boot = 1;
+				break;
+			case 'n':
+				no_erase = 1;
+				break;
+			case 'j':
+				jffs2file = optarg;
+				break;
+			case 's':
+				errno = 0;
+				jffs2_skip_bytes = strtoul(optarg, 0, 0);
+				if (errno) {
+						fprintf(stderr, "-s: illegal numeric string\n");
+						usage();
+				}
+				break;
+			case 'q':
+				quiet++;
+				break;
+			case 'e':
+				i = 0;
+				while ((erase[i] != NULL) && ((i + 1) < MAX_ARGS))
+					i++;
+
+				erase[i++] = optarg;
+				erase[i] = NULL;
+				break;
+			case 'd':
+				jffs2dir = optarg;
+				break;
+			case 'p':
+				errno = 0;
+				part_offset = strtoul(optarg, 0, 0);
+				if (errno) {
+					fprintf(stderr, "-p: illegal numeric string\n");
+					usage();
+				}
+				break;
+			case 'l':
+				errno = 0;
+				dump_len = strtoul(optarg, 0, 0);
+				if (errno) {
+					fprintf(stderr, "-l: illegal numeric string\n");
+					usage();
+				}
+				break;
+			case 'o':
+				errno = 0;
+				offset = strtoul(optarg, 0, 0);
+				if (errno) {
+					fprintf(stderr, "-o: illegal numeric string\n");
+					usage();
+				}
+				break;
+			case 'c':
+				errno = 0;
+				data_size = strtoul(optarg, 0, 0);
+				if (errno) {
+					fprintf(stderr, "-c: illegal numeric string\n");
+					usage();
+				}
+				break;
+			case 't':
+				tpl_uboot_args_part = optarg;
+				break;
+#ifdef FIS_SUPPORT
+			case 'F':
+				fis_layout = optarg;
+				break;
+#endif
+			case '?':
+			default:
+				usage();
+		}
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 2)
+		usage();
+
+	if ((strcmp(argv[0], "unlock") == 0) && (argc == 2)) {
+		cmd = CMD_UNLOCK;
+		device = argv[1];
+	} else if ((strcmp(argv[0], "erase") == 0) && (argc == 2)) {
+		cmd = CMD_ERASE;
+		device = argv[1];
+	} else if (((strcmp(argv[0], "resetbc") == 0) && (argc == 2)) && mtd_resetbc) {
+		cmd = CMD_RESETBC;
+		device = argv[1];
+	} else if (((strcmp(argv[0], "fixtrx") == 0) && (argc == 2)) && mtd_fixtrx) {
+		cmd = CMD_FIXTRX;
+		device = argv[1];
+	} else if (((strcmp(argv[0], "fixseama") == 0) && (argc == 2)) && mtd_fixseama) {
+		cmd = CMD_FIXSEAMA;
+		device = argv[1];
+	} else if (((strcmp(argv[0], "fixwrg") == 0) && (argc == 2)) && mtd_fixwrg) {
+		cmd = CMD_FIXWRG;
+		device = argv[1];
+	} else if (((strcmp(argv[0], "fixwrgg") == 0) && (argc == 2)) && mtd_fixwrgg) {
+		cmd = CMD_FIXWRGG;
+		device = argv[1];
+	} else if ((strcmp(argv[0], "verify") == 0) && (argc == 3)) {
+		cmd = CMD_VERIFY;
+		imagefile = argv[1];
+		device = argv[2];
+	} else if ((strcmp(argv[0], "dump") == 0) && (argc == 2)) {
+		cmd = CMD_DUMP;
+		device = argv[1];
+	} else if ((strcmp(argv[0], "write") == 0) && (argc == 3)) {
+		cmd = CMD_WRITE;
+		device = argv[2];
+
+		if (strcmp(argv[1], "-") == 0) {
+			imagefile = "<stdin>";
+			imagefd = 0;
+		} else {
+			imagefile = argv[1];
+			if ((imagefd = open(argv[1], O_RDONLY)) < 0) {
+				fprintf(stderr, "Couldn't open image file: %s!\n", imagefile);
+				exit(1);
+			}
+		}
+
+		if (!mtd_check(device)) {
+			fprintf(stderr, "Can't open device for writing!\n");
+			exit(1);
+		}
+		/* check trx file before erasing or writing anything */
+		if (!image_check(imagefd, device) && !force) {
+			fprintf(stderr, "Image check failed.\n");
+			exit(1);
+		}
+	} else if ((strcmp(argv[0], "jffs2write") == 0) && (argc == 3)) {
+		cmd = CMD_JFFS2WRITE;
+		device = argv[2];
+
+		imagefile = argv[1];
+		if (!mtd_check(device)) {
+			fprintf(stderr, "Can't open device for writing!\n");
+			exit(1);
+		}
+	} else {
+		usage();
+	}
+
+	sync();
+
+	i = 0;
+	unlocked = 0;
+	while (erase[i] != NULL) {
+		mtd_unlock(erase[i]);
+		mtd_erase(erase[i]);
+		if (strcmp(erase[i], device) == 0)
+			unlocked = 1;
+		i++;
+	}
+
+	switch (cmd) {
+		case CMD_UNLOCK:
+			if (!unlocked)
+				mtd_unlock(device);
+			break;
+		case CMD_VERIFY:
+			mtd_verify(device, imagefile);
+			break;
+		case CMD_DUMP:
+			mtd_dump(device, offset, dump_len);
+			break;
+		case CMD_ERASE:
+			if (!unlocked)
+				mtd_unlock(device);
+			mtd_erase(device);
+			break;
+		case CMD_WRITE:
+			if (!unlocked)
+				mtd_unlock(device);
+			mtd_write(imagefd, device, fis_layout, part_offset);
+			break;
+		case CMD_JFFS2WRITE:
+			if (!unlocked)
+				mtd_unlock(device);
+			mtd_write_jffs2(device, imagefile, jffs2dir);
+			break;
+		case CMD_FIXTRX:
+			if (mtd_fixtrx) {
+				mtd_fixtrx(device, offset, data_size);
+			}
+			break;
+		case CMD_RESETBC:
+			if (mtd_resetbc) {
+				mtd_resetbc(device);
+			}
+			break;
+		case CMD_FIXSEAMA:
+			if (mtd_fixseama)
+				mtd_fixseama(device, 0, data_size);
+			break;
+		case CMD_FIXWRG:
+			if (mtd_fixwrg)
+				mtd_fixwrg(device, 0, data_size);
+			break;
+		case CMD_FIXWRGG:
+			if (mtd_fixwrgg)
+				mtd_fixwrgg(device, 0, data_size);
+			break;
+	}
+
+	sync();
+
+	if (boot)
+		do_reboot();
+
+	return 0;
+}
diff --git a/recipes-devtools/mtd/files/src/mtd.h b/recipes-devtools/mtd/files/src/mtd.h
new file mode 100644
index 0000000..e51aa53
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/mtd.h
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef __mtd_h
+#define __mtd_h
+
+#include <stdbool.h>
+
+#if defined(target_bcm47xx) || defined(target_bcm53xx)
+#define target_brcm 1
+#endif
+
+#define JFFS2_EOF "\xde\xad\xc0\xde"
+
+extern int quiet;
+extern int mtdsize;
+extern int erasesize;
+
+extern int mtd_open(const char *mtd, bool block);
+extern int mtd_check_open(const char *mtd);
+extern int mtd_block_is_bad(int fd, int offset);
+extern int mtd_erase_block(int fd, int offset);
+extern int mtd_write_buffer(int fd, const char *buf, int offset, int length);
+extern int mtd_write_jffs2(const char *mtd, const char *filename, const char *dir);
+extern int mtd_replace_jffs2(const char *mtd, int fd, int ofs, const char *filename);
+extern void mtd_parse_jffs2data(const char *buf, const char *dir);
+
+/* target specific functions */
+extern int trx_fixup(int fd, const char *name)  __attribute__ ((weak));
+extern int trx_check(int imagefd, const char *mtd, char *buf, int *len) __attribute__ ((weak));
+extern int mtd_fixtrx(const char *mtd, size_t offset, size_t data_size) __attribute__ ((weak));
+extern int mtd_fixseama(const char *mtd, size_t offset, size_t data_size) __attribute__ ((weak));
+extern int mtd_fixwrg(const char *mtd, size_t offset, size_t data_size) __attribute__ ((weak));
+extern int mtd_fixwrgg(const char *mtd, size_t offset, size_t data_size) __attribute__ ((weak));
+extern int mtd_resetbc(const char *mtd) __attribute__ ((weak));
+extern int mtd_tpl_recoverflag_write(const char *mtd, const bool recovery_active) __attribute__ ((weak));
+#endif /* __mtd_h */
diff --git a/recipes-devtools/mtd/files/src/seama.c b/recipes-devtools/mtd/files/src/seama.c
new file mode 100644
index 0000000..6e47811
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/seama.c
@@ -0,0 +1,190 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * seama.c
+ *
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ *
+ * Based on the trx fixup code:
+ *   Copyright (C) 2005 Mike Baker
+ *   Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include "mtd.h"
+#include "seama.h"
+#include "md5.h"
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define STORE32_LE(X)           ((((X) & 0x000000FF) << 24) | (((X) & 0x0000FF00) << 8) | (((X) & 0x00FF0000) >> 8) | (((X) & 0xFF000000) >> 24))
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define STORE32_LE(X)           (X)
+#else
+#error unknown endianness!
+#endif
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+int
+seama_fix_md5(struct seama_entity_header *shdr, int fd, size_t data_offset, size_t data_size)
+{
+	char *buf;
+	ssize_t res;
+	MD5_CTX ctx;
+	unsigned char digest[16];
+	int i;
+	int err = 0;
+
+	buf = malloc(data_size);
+	if (!buf) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	res = pread(fd, buf, data_size, data_offset);
+	if (res != data_size) {
+		perror("pread");
+		err = -EIO;
+		goto err_free;
+	}
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, buf, data_size);
+	MD5_Final(digest, &ctx);
+
+	if (!memcmp(digest, shdr->md5, sizeof(digest))) {
+		if (quiet < 2)
+			fprintf(stderr, "the header is fixed already\n");
+		return -1;
+	}
+
+	if (quiet < 2) {
+		fprintf(stderr, "new size:%u, new MD5: ", data_size);
+		for (i = 0; i < sizeof(digest); i++)
+			fprintf(stderr, "%02x", digest[i]);
+
+		fprintf(stderr, "\n");
+	}
+
+	/* update the size in the image */
+	shdr->size = htonl(data_size);
+
+	/* update the checksum in the image */
+	memcpy(shdr->md5, digest, sizeof(digest));
+
+err_free:
+	free(buf);
+err_out:
+	return err;
+}
+
+int
+mtd_fixseama(const char *mtd, size_t offset, size_t data_size)
+{
+	int fd;
+	char *first_block;
+	ssize_t res;
+	size_t block_offset;
+	size_t data_offset;
+	struct seama_entity_header *shdr;
+
+	if (quiet < 2)
+		fprintf(stderr, "Trying to fix SEAMA header in %s at 0x%x...\n",
+			mtd, offset);
+
+	block_offset = offset & ~(erasesize - 1);
+	offset -= block_offset;
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	if (block_offset + erasesize > mtdsize) {
+		fprintf(stderr, "Offset too large, device size 0x%x\n",
+			mtdsize);
+		exit(1);
+	}
+
+	first_block = malloc(erasesize);
+	if (!first_block) {
+		perror("malloc");
+		exit(1);
+	}
+
+	res = pread(fd, first_block, erasesize, block_offset);
+	if (res != erasesize) {
+		perror("pread");
+		exit(1);
+	}
+
+	shdr = (struct seama_entity_header *)(first_block + offset);
+	if (shdr->magic != htonl(SEAMA_MAGIC)) {
+		fprintf(stderr, "No SEAMA header found\n");
+		exit(1);
+	} else if (!ntohl(shdr->size)) {
+		fprintf(stderr, "Seama entity with empty image\n");
+		exit(1);
+	}
+
+	data_offset = offset + sizeof(struct seama_entity_header) + ntohs(shdr->metasize);
+	if (!data_size)
+		data_size = mtdsize - data_offset;
+	if (data_size > ntohl(shdr->size))
+		data_size = ntohl(shdr->size);
+	if (seama_fix_md5(shdr, fd, data_offset, data_size))
+		goto out;
+
+	if (mtd_erase_block(fd, block_offset)) {
+		fprintf(stderr, "Can't erease block at 0x%x (%s)\n",
+			block_offset, strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Rewriting block at 0x%x\n", block_offset);
+
+	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
+		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Done.\n");
+
+out:
+	close (fd);
+	sync();
+
+	return 0;
+}
+
diff --git a/recipes-devtools/mtd/files/src/seama.h b/recipes-devtools/mtd/files/src/seama.h
new file mode 100644
index 0000000..8c0d4ec
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/seama.h
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/* vi: set sw=4 ts=4: */
+/*
+ *	(SEA)ttle i(MA)ge is the image which used in project seattle.
+ *
+ *	Created by David Hsieh <david_hsieh@alphanetworks.com>
+ *	Copyright (C) 2008-2009 Alpha Networks, Inc.
+ *
+ *	This file is free software; you can redistribute it and/or
+ *	modify it under the terms of the GNU Lesser General Public
+ *	License as published by the Free Software Foundation; either'
+ *	version 2.1 of the License, or (at your option) any later version.
+ *
+ *	The GNU C Library is distributed in the hope that it will be useful,'
+ *	but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *	Lesser General Public License for more details.
+ *
+ *	You should have received a copy of the GNU Lesser General Public
+ *	License along with the GNU C Library; if not, write to the Free
+ *	Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ *	02111-1307 USA.
+ */
+
+#ifndef __SEAMA_HEADER_FILE__
+#define __SEAMA_HEADER_FILE__
+
+#include <stdint.h>
+
+#define SEAMA_MAGIC		0x5EA3A417
+
+/*
+ *	SEAMA looks like the following map.
+ *	All the data of the header should be in network byte order.
+ *
+ *  +-------------+-------------+------------
+ *	| SEAMA magic               |     ^
+ *  +-------------+-------------+     |
+ *	| reserved    | meta size   |     |
+ *  +-------------+-------------+   header
+ *	| image size (0 bytes)      |     |
+ *  +-------------+-------------+     |
+ *	~ Meta data                 ~     v
+ *  +-------------+-------------+------------
+ *	| SEAMA magic               |   ^     ^
+ *  +-------------+-------------+   |     |
+ *	| reserved    | meta size   |   |     |
+ *  +-------------+-------------+   |     |
+ *	| image size                |   |     |
+ *  +-------------+-------------+ header  |
+ *	|                           |   |     |
+ *	| 16 bytes of MD5 digest    |   |     |
+ *	|                           |   |     |
+ *	|                           |   |     |
+ *  +-------------+-------------+   |     |
+ *	~ Meta data                 ~   v     |
+ *  +-------------+-------------+-------  |
+ *	|                           |         |
+ *	| Image of the 1st entity   |         |
+ *	~                           ~ 1st entity
+ *	|                           |         |
+ *	|                           |         v
+ *  +-------------+-------------+-------------
+ *	| SEAMA magic               |   ^     ^
+ *  +-------------+-------------+   |     |
+ *	| reserved    | meta size   |   |     |
+ *  +-------------+-------------+   |     |
+ *	| image size                |   |     |
+ *  +-------------+-------------+ header  |
+ *	|                           |   |     |
+ *	| 16 bytes of MD5 digest    |   |     |
+ *	|                           |   |     |
+ *	|                           |   |     |
+ *  +-------------+-------------+   |     |
+ *	~ Meta data                 ~   v     |
+ *  +-------------+-------------+-------  |
+ *	|                           |         |
+ *	| Image of the 2nd entity   |         |
+ *	~                           ~ 2nd entity
+ *	|                           |         |
+ *	|                           |         v
+ *  +-------------+-------------+-------------
+ */
+
+
+/*
+ *	SEAMA header
+ *
+ *	|<-------- 32 bits -------->|
+ *  +-------------+-------------+
+ *	| SEAMA magic               |
+ *  +-------------+-------------+
+ *	| reserved    | meta size   |
+ *  +-------------+-------------+
+ *	| image size                |
+ *  +-------------+-------------+
+ */
+
+/* seama header */
+struct seama_entity_header {
+	uint32_t	magic;			/* should always be SEAMA_MAGIC. */
+	uint16_t	reserved;		/* reserved for  */
+	uint16_t	metasize;		/* size of the META data */
+	uint32_t	size;			/* size of the image */
+	uint8_t		md5[16];
+} __attribute__ ((packed));
+
+
+#endif
diff --git a/recipes-devtools/mtd/files/src/tpl_ramips_recoveryflag.c b/recipes-devtools/mtd/files/src/tpl_ramips_recoveryflag.c
new file mode 100644
index 0000000..a52f73f
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/tpl_ramips_recoveryflag.c
@@ -0,0 +1,96 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * TP-Link recovery flag set and unset code for ramips target
+ *
+ * Copyright (C) 2018 David Bauer <mail@david-bauer.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License v2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include <stdint.h>
+
+#include <mtd/mtd-user.h>
+#include <sys/ioctl.h>
+
+#include "mtd.h"
+
+
+#define TPL_RECOVER_MAGIC	0x89abcdef
+#define TPL_NO_RECOVER_MAGIC	0x00000000
+
+
+struct uboot_args {
+	uint32_t magic;
+};
+
+int mtd_tpl_recoverflag_write(const char *mtd, const bool recovery_active)
+{
+	struct erase_info_user erase_info;
+	struct uboot_args *args;
+	uint32_t magic;
+	int ret = 0;
+	int fd;
+
+	args = malloc(erasesize);
+	if (!args) {
+		fprintf(stderr, "Could not allocate memory!\n");
+		return -1;
+	}
+
+	fd = mtd_check_open(mtd);
+	if (fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		ret = -1;
+		goto out;
+	}
+
+	/* read first block (containing the magic) */
+	pread(fd, args, erasesize, 0);
+
+	/* set magic to desired value */
+	magic = TPL_RECOVER_MAGIC;
+	if (!recovery_active)
+		magic = TPL_NO_RECOVER_MAGIC;
+
+	/* no need to write when magic is already set correctly */
+	if (magic == args->magic)
+		goto out;
+
+	/* erase first block (containing the magic) */
+	erase_info.start = 0;
+	erase_info.length = erasesize;
+
+	ret = ioctl(fd, MEMERASE, &erase_info);
+	if (ret < 0) {
+		fprintf(stderr, "failed to erase block: %i\n", ret);
+		goto out;
+	}
+
+	/* write magic to flash */
+	args->magic = magic;
+
+	ret = pwrite(fd, args, erasesize, 0);
+	if (ret < 0)
+		fprintf(stderr, "failed to write: %i\n", ret);
+
+	sync();
+out:
+	free(args);
+	close(fd);
+
+	return ret;
+}
diff --git a/recipes-devtools/mtd/files/src/trx.c b/recipes-devtools/mtd/files/src/trx.c
new file mode 100644
index 0000000..83e5652
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/trx.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * trx.c
+ *
+ * Copyright (C) 2005 Mike Baker
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <endian.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include "mtd.h"
+#include "crc32.h"
+
+#define TRX_MAGIC       0x30524448      /* "HDR0" */
+#define TRX_CRC32_DATA_OFFSET	12	/* First 12 bytes are not covered by CRC32 */
+#define TRX_CRC32_DATA_SIZE	16
+struct trx_header {
+	uint32_t magic;		/* "HDR0" */
+	uint32_t len;		/* Length of file including header */
+	uint32_t crc32;		/* 32-bit CRC from flag_version to end of file */
+	uint32_t flag_version;	/* 0:15 flags, 16:31 version */
+	uint32_t offsets[3];    /* Offsets of partitions from start of header */
+};
+
+#define min(x,y) ({		\
+	typeof(x) _x = (x);	\
+	typeof(y) _y = (y);	\
+	(void) (&_x == &_y);	\
+	_x < _y ? _x : _y; })
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define STORE32_LE(X)           ((((X) & 0x000000FF) << 24) | (((X) & 0x0000FF00) << 8) | (((X) & 0x00FF0000) >> 8) | (((X) & 0xFF000000) >> 24))
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define STORE32_LE(X)           (X)
+#else
+#error unknown endianness!
+#endif
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+int
+trx_fixup(int fd, const char *name)
+{
+	struct mtd_info_user mtdInfo;
+	unsigned long len;
+	struct trx_header *trx;
+	void *ptr, *scan;
+	int bfd;
+
+	if (ioctl(fd, MEMGETINFO, &mtdInfo) < 0) {
+		fprintf(stderr, "Failed to get mtd info\n");
+		goto err;
+	}
+
+	len = mtdInfo.size;
+	if (mtdInfo.size <= 0) {
+		fprintf(stderr, "Invalid MTD device size\n");
+		goto err;
+	}
+
+	bfd = mtd_open(name, true);
+	ptr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, bfd, 0);
+	if (!ptr || (ptr == (void *) -1)) {
+		perror("mmap");
+		fprintf(stderr, "Mapping the TRX header failed\n");
+		goto err1;
+	}
+
+	trx = ptr;
+	if (trx->magic != TRX_MAGIC) {
+		fprintf(stderr, "TRX header not found\n");
+		goto err;
+	}
+
+	scan = ptr + offsetof(struct trx_header, flag_version);
+	trx->crc32 = crc32buf(scan, trx->len - (scan - ptr));
+	msync(ptr, sizeof(struct trx_header), MS_SYNC|MS_INVALIDATE);
+	munmap(ptr, len);
+	close(bfd);
+	return 0;
+
+err1:
+	close(bfd);
+err:
+	return -1;
+}
+
+int
+trx_check(int imagefd, const char *mtd, char *buf, int *len)
+{
+	const struct trx_header *trx = (const struct trx_header *) buf;
+	int fd;
+
+	if (strcmp(mtd, "firmware") != 0)
+		return 1;
+
+	if (*len < 32) {
+		*len += read(imagefd, buf + *len, 32 - *len);
+		if (*len < 32) {
+			fprintf(stdout, "Could not get image header, file too small (%d bytes)\n", *len);
+			return 0;
+		}
+	}
+
+	if (trx->magic != TRX_MAGIC || trx->len < sizeof(struct trx_header)) {
+		if (quiet < 2) {
+			fprintf(stderr, "Bad trx header\n");
+			fprintf(stderr, "This is not the correct file format; refusing to flash.\n"
+					"Please specify the correct file or use -f to force.\n");
+		}
+		return 0;
+	}
+
+	/* check if image fits to mtd device */
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	if(mtdsize < trx->len) {
+		fprintf(stderr, "Image too big for partition: %s\n", mtd);
+		close(fd);
+		return 0;
+	}
+
+	close(fd);
+	return 1;
+}
+
+int
+mtd_fixtrx(const char *mtd, size_t offset, size_t data_size)
+{
+	size_t data_offset;
+	int fd;
+	struct trx_header *trx;
+	char *first_block;
+	char *buf, *to;
+	ssize_t res;
+	size_t block_offset;
+
+	if (quiet < 2)
+		fprintf(stderr, "Trying to fix trx header in %s at 0x%x...\n", mtd, offset);
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	data_offset = offset + TRX_CRC32_DATA_OFFSET;
+	if (data_size)
+		data_size += TRX_CRC32_DATA_SIZE;
+	else
+		data_size = erasesize - TRX_CRC32_DATA_OFFSET;
+
+	block_offset = offset & ~(erasesize - 1);
+	offset -= block_offset;
+
+	if (data_offset + data_size > mtdsize) {
+		fprintf(stderr, "Offset too large, device size 0x%x\n", mtdsize);
+		exit(1);
+	}
+
+	first_block = malloc(erasesize);
+	if (!first_block) {
+		perror("malloc");
+		exit(1);
+	}
+
+	res = pread(fd, first_block, erasesize, block_offset);
+	if (res != erasesize) {
+		perror("pread");
+		exit(1);
+	}
+
+	trx = (struct trx_header *)(first_block + offset);
+	if (trx->magic != STORE32_LE(0x30524448)) {
+		fprintf(stderr, "No trx magic found\n");
+		exit(1);
+	}
+
+	buf = malloc(data_size);
+	if (!buf) {
+		perror("malloc");
+		exit(1);
+	}
+
+	to = buf;
+	while (data_size) {
+		size_t read_block_offset = data_offset & ~(erasesize - 1);
+		size_t read_chunk;
+
+		read_chunk = erasesize - (data_offset & (erasesize - 1));
+		read_chunk = min(read_chunk, data_size);
+
+		/* Read from good blocks only to match CFE behavior */
+		if (!mtd_block_is_bad(fd, read_block_offset)) {
+			res = pread(fd, to, read_chunk, data_offset);
+			if (res != read_chunk) {
+				perror("pread");
+				exit(1);
+			}
+			to += read_chunk;
+		}
+
+		data_offset += read_chunk;
+		data_size -= read_chunk;
+	}
+	data_size = to - buf;
+
+	if (trx->len == STORE32_LE(data_size + TRX_CRC32_DATA_OFFSET) &&
+	    trx->crc32 == STORE32_LE(crc32buf(buf, data_size))) {
+		if (quiet < 2)
+			fprintf(stderr, "Header already fixed, exiting\n");
+		close(fd);
+		return 0;
+	}
+
+	trx->len = STORE32_LE(data_size + offsetof(struct trx_header, flag_version));
+
+	trx->crc32 = STORE32_LE(crc32buf(buf, data_size));
+	if (mtd_erase_block(fd, block_offset)) {
+		fprintf(stderr, "Can't erease block at 0x%x (%s)\n", block_offset, strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "New crc32: 0x%x, rewriting block\n", trx->crc32);
+
+	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
+		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Done.\n");
+
+	close (fd);
+	sync();
+	return 0;
+
+}
diff --git a/recipes-devtools/mtd/files/src/wrg.c b/recipes-devtools/mtd/files/src/wrg.c
new file mode 100644
index 0000000..8844ddb
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/wrg.c
@@ -0,0 +1,210 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * wrg.c
+ *
+ * Copyright (C) 2005 Mike Baker
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
+ * Copyright (C) 2017 George Hopkins <george-hopkins@null.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <byteswap.h>
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include "mtd.h"
+#include "md5.h"
+
+#if !defined(__BYTE_ORDER)
+#error "Unknown byte order"
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define cpu_to_le32(x)	bswap_32(x)
+#define le32_to_cpu(x)	bswap_32(x)
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+#define cpu_to_le32(x)	(x)
+#define le32_to_cpu(x)	(x)
+#else
+#error "Unsupported endianness"
+#endif
+
+#define WRG_MAGIC	0x20040220
+
+struct wrg_header {
+	char		signature[32];
+	uint32_t	magic1;
+	uint32_t	magic2;
+	uint32_t	size;
+	uint32_t	offset;
+	char		devname[32];
+	char		digest[16];
+} __attribute__ ((packed));
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+int
+wrg_fix_md5(struct wrg_header *shdr, int fd, size_t data_offset, size_t data_size)
+{
+	char *buf;
+	ssize_t res;
+	MD5_CTX ctx;
+	unsigned char digest[16];
+	int i;
+	int err = 0;
+
+	buf = malloc(data_size);
+	if (!buf) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	res = pread(fd, buf, data_size, data_offset);
+	if (res != data_size) {
+		perror("pread");
+		err = -EIO;
+		goto err_free;
+	}
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, (char *)&shdr->offset, sizeof(shdr->offset));
+	MD5_Update(&ctx, (char *)&shdr->devname, sizeof(shdr->devname));
+	MD5_Update(&ctx, buf, data_size);
+	MD5_Final(digest, &ctx);
+
+	if (!memcmp(digest, shdr->digest, sizeof(digest))) {
+		if (quiet < 2)
+			fprintf(stderr, "the header is fixed already\n");
+		return -1;
+	}
+
+	if (quiet < 2) {
+		fprintf(stderr, "new size: %u, new MD5: ", data_size);
+		for (i = 0; i < sizeof(digest); i++)
+			fprintf(stderr, "%02x", digest[i]);
+
+		fprintf(stderr, "\n");
+	}
+
+	/* update the size in the image */
+	shdr->size = cpu_to_le32(data_size);
+
+	/* update the checksum in the image */
+	memcpy(shdr->digest, digest, sizeof(digest));
+
+err_free:
+	free(buf);
+err_out:
+	return err;
+}
+
+int
+mtd_fixwrg(const char *mtd, size_t offset, size_t data_size)
+{
+	int fd;
+	char *first_block;
+	ssize_t res;
+	size_t block_offset;
+	size_t data_offset;
+	struct wrg_header *shdr;
+
+	if (quiet < 2)
+		fprintf(stderr, "Trying to fix WRG header in %s at 0x%x...\n",
+			mtd, offset);
+
+	block_offset = offset & ~(erasesize - 1);
+	offset -= block_offset;
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	if (block_offset + erasesize > mtdsize) {
+		fprintf(stderr, "Offset too large, device size 0x%x\n",
+			mtdsize);
+		exit(1);
+	}
+
+	first_block = malloc(erasesize);
+	if (!first_block) {
+		perror("malloc");
+		exit(1);
+	}
+
+	res = pread(fd, first_block, erasesize, block_offset);
+	if (res != erasesize) {
+		perror("pread");
+		exit(1);
+	}
+
+	shdr = (struct wrg_header *)(first_block + offset);
+	if (le32_to_cpu(shdr->magic1) != WRG_MAGIC) {
+		fprintf(stderr, "No WRG header found (%08x != %08x)\n",
+		        le32_to_cpu(shdr->magic1), WRG_MAGIC);
+		exit(1);
+	} else if (!le32_to_cpu(shdr->size)) {
+		fprintf(stderr, "WRG entity with empty image\n");
+		exit(1);
+	}
+
+	data_offset = offset + sizeof(struct wrg_header);
+	if (!data_size)
+		data_size = mtdsize - data_offset;
+	if (data_size > le32_to_cpu(shdr->size))
+		data_size = le32_to_cpu(shdr->size);
+	if (wrg_fix_md5(shdr, fd, data_offset, data_size))
+		goto out;
+
+	if (mtd_erase_block(fd, block_offset)) {
+		fprintf(stderr, "Can't erease block at 0x%x (%s)\n",
+			block_offset, strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Rewriting block at 0x%x\n", block_offset);
+
+	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
+		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Done.\n");
+
+out:
+	close (fd);
+	sync();
+
+	return 0;
+}
diff --git a/recipes-devtools/mtd/files/src/wrgg.c b/recipes-devtools/mtd/files/src/wrgg.c
new file mode 100644
index 0000000..593e99f
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/wrgg.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * wrgg.c
+ *
+ * Copyright (C) 2005 Mike Baker
+ * Copyright (C) 2008 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2011-2012 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2016 Stijn Tintel <stijn@linux-ipv6.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <arpa/inet.h>
+
+#include <sys/ioctl.h>
+#include <mtd/mtd-user.h>
+#include "mtd.h"
+#include "wrgg.h"
+#include "md5.h"
+
+static inline uint32_t le32_to_cpu(uint8_t *buf)
+{
+	return buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24;
+}
+
+ssize_t pread(int fd, void *buf, size_t count, off_t offset);
+ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset);
+
+int
+wrgg_fix_md5(struct wrgg03_header *shdr, int fd, size_t data_offset, size_t data_size)
+{
+	char *buf;
+	ssize_t res;
+	MD5_CTX ctx;
+	unsigned char digest[16];
+	int i;
+	int err = 0;
+
+	buf = malloc(data_size);
+	if (!buf) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	res = pread(fd, buf, data_size, data_offset);
+	if (res != data_size) {
+		perror("pread");
+		err = -EIO;
+		goto err_free;
+	}
+
+	MD5_Init(&ctx);
+	MD5_Update(&ctx, (char *)&shdr->offset, sizeof(shdr->offset));
+	MD5_Update(&ctx, (char *)&shdr->dev_name, sizeof(shdr->dev_name));
+	MD5_Update(&ctx, buf, data_size);
+	MD5_Final(digest, &ctx);
+
+	if (!memcmp(digest, shdr->digest, sizeof(digest))) {
+		if (quiet < 2)
+			fprintf(stderr, "the header is fixed already\n");
+		return -1;
+	}
+
+	if (quiet < 2) {
+		fprintf(stderr, "new size:%u, new MD5: ", data_size);
+		for (i = 0; i < sizeof(digest); i++)
+			fprintf(stderr, "%02x", digest[i]);
+
+		fprintf(stderr, "\n");
+	}
+
+	/* update the size in the image */
+	shdr->size = data_size;
+
+	/* update the checksum in the image */
+	memcpy(shdr->digest, digest, sizeof(digest));
+
+err_free:
+	free(buf);
+err_out:
+	return err;
+}
+
+int
+mtd_fixwrgg(const char *mtd, size_t offset, size_t data_size)
+{
+	int fd;
+	char *first_block;
+	ssize_t res;
+	size_t block_offset;
+	size_t data_offset;
+	struct wrgg03_header *shdr;
+
+	if (quiet < 2)
+		fprintf(stderr, "Trying to fix WRGG header in %s at 0x%x...\n",
+			mtd, offset);
+
+	block_offset = offset & ~(erasesize - 1);
+	offset -= block_offset;
+
+	fd = mtd_check_open(mtd);
+	if(fd < 0) {
+		fprintf(stderr, "Could not open mtd device: %s\n", mtd);
+		exit(1);
+	}
+
+	if (block_offset + erasesize > mtdsize) {
+		fprintf(stderr, "Offset too large, device size 0x%x\n",
+			mtdsize);
+		exit(1);
+	}
+
+	first_block = malloc(erasesize);
+	if (!first_block) {
+		perror("malloc");
+		exit(1);
+	}
+
+	res = pread(fd, first_block, erasesize, block_offset);
+	if (res != erasesize) {
+		perror("pread");
+		exit(1);
+	}
+
+	shdr = (struct wrgg03_header *)(first_block + offset);
+
+	/* The magic is always stored in little-endian byte order */
+	if (le32_to_cpu((uint8_t *)&shdr->magic1) != WRGG03_MAGIC) {
+		fprintf(stderr, "magic1 = %x\n", shdr->magic1);
+		fprintf(stderr, "WRGG03_MAGIC = %x\n", WRGG03_MAGIC);
+		fprintf(stderr, "No WRGG header found\n");
+		exit(1);
+	} else if (!shdr->size) {
+		fprintf(stderr, "WRGG entity with empty image\n");
+		exit(1);
+	}
+
+	data_offset = offset + sizeof(struct wrgg03_header);
+	if (!data_size)
+		data_size = mtdsize - data_offset;
+	if (data_size > shdr->size)
+		data_size = shdr->size;
+	if (wrgg_fix_md5(shdr, fd, data_offset, data_size))
+		goto out;
+
+	if (mtd_erase_block(fd, block_offset)) {
+		fprintf(stderr, "Can't erease block at 0x%x (%s)\n",
+			block_offset, strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Rewriting block at 0x%x\n", block_offset);
+
+	if (pwrite(fd, first_block, erasesize, block_offset) != erasesize) {
+		fprintf(stderr, "Error writing block (%s)\n", strerror(errno));
+		exit(1);
+	}
+
+	if (quiet < 2)
+		fprintf(stderr, "Done.\n");
+
+out:
+	close (fd);
+	sync();
+
+	return 0;
+}
diff --git a/recipes-devtools/mtd/files/src/wrgg.h b/recipes-devtools/mtd/files/src/wrgg.h
new file mode 100644
index 0000000..f47ce04
--- /dev/null
+++ b/recipes-devtools/mtd/files/src/wrgg.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef __wrgg_h
+#define __wrgg_h
+
+#define WRGG03_MAGIC	0x20080321
+
+struct wrgg03_header {
+	char		signature[32];
+	uint32_t	magic1;
+	uint32_t	magic2;
+	char		version[16];
+	char		model[16];
+	uint32_t	flag[2];
+	uint32_t	reserve[2];
+	char		buildno[16];
+	uint32_t	size;
+	uint32_t	offset;
+	char		dev_name[32];
+	char		digest[16];
+} __attribute__ ((packed));
+#endif /* __wrgg_h */
diff --git a/recipes-devtools/mtd/mtd.bb b/recipes-devtools/mtd/mtd.bb
new file mode 100644
index 0000000..4006638
--- /dev/null
+++ b/recipes-devtools/mtd/mtd.bb
@@ -0,0 +1,33 @@
+#Basic Configuration
+DESCRIPTION = "Mtd tool"
+SECTION = "base"
+LICENSE = "GPLv2"
+LIC_FILES_CHKSUM = "file://COPYING;md5=c188eeeb69c0a05d0545816f1458a0c9"
+
+DEPENDS += "libubox"
+
+inherit autotools systemd pkgconfig
+
+SRC_URI = " \
+    file://COPYING;subdir=git/src \
+    file://src;subdir=git \
+    "
+
+S = "${WORKDIR}/git/src"
+
+META_LDFLAGS_ADD = "-lubox"
+
+LDFLAGS_remove = "-Wl,--as-needed"
+
+CFLAGS_append = " -Wall -flto"
+LDFLAGS_append = " -flto=jobserver ${META_LDFLAGS_ADD}"
+
+do_compile_prepend(){
+    cd ${S}
+}
+
+do_install() {
+    install -d ${D}/usr/bin/
+    install -m 0755 ${S}/mtd ${D}/usr/bin/
+}
+