ipq40xx: add factory image for EnGenius ENS620EXT

Extended  mksenaofw to support new "capwap" header structure.
This supports flashing from factory 3.0.0, 3.0.1, 3.1.0 and 3.5.5
firmware.

Note that the factory image format changes for 3.1 and later firmware,
and that the 3.1.0 and 3.5.5 Engenius firmware will refuse the
factory_30.bin file. Similarly, the 3.0.0 and 3.0.1 Engenius firmware
will refuse the factory_35.bin file.

Flashing from the Engenius 3.1.0 firmware with the factory_35.bin
firmware has not been tested, as 3.1.0 firmware (Engenius "middleFW")
is only intended as part of the upgrade path to 3.5.5 firmware.

Modified ipq40xx image Makefile to appropriately invoke mksenaofw
with new parameters to configure the capwap header.

Note that there is currently no method to return to factory firmware,
so this is a one-way street.

Path from factory 3.0.0 and 3.0.1 (EnGenius) software to OpenWrt is
to navigate to 192.168.1.1 on the stock firmware and navigate to the
firmware menu. Then copy the URL you have for that page, something like
http://192.168.1.1/cgi-bin/luci/;stok=12345abcdef/admin/system/flashops
and replace the trailing /admin/system/flashops with just /easyflashops

You should then be presented with a simple "Firmware Upgrade" page.
On that page, BE SURE TO CLEAR the "Keep Settings:" checkbox.

Choose the openwrt-ipq40xx-engenius_ens620ext-squashfs-factory_30.bin,
click "Upgrade" and on the following page select "Proceed".

Path from factory 3.5.5 (EnGenius) software to OpenWrt is simply to
use the stock firmware update menu. Choose the
openwrt-ipq40xx-engenius_ens620ext-squashfs-factory_35.bin and click
"Upload" and "Proceed".

The device should then flash the OpenWrt firmware and reboot. Note
that this resets the device to a default configuration with Wi-Fi
disabled, LAN1/PoE acting as a WAN port (running DHCP client) and LAN2
acting as a LAN port with a DHCP server on 192.168.1.x (AP is at
192.168.1.1)

Signed-off-by: Steve Glennon <s.glennon@cablelabs.com>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
[sorry, for unfixing the 80-lines eyesores.]
openwrt-19.07
Steve Glennon 2019-04-09 14:46:32 -06:00 committed by Christian Lamparter
parent 9b0eb4d260
commit 6411eac5da
3 changed files with 208 additions and 40 deletions

View File

@ -51,6 +51,14 @@ define Device/DniImage
endef endef
DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_HW_ID DEVICE_VARS += NETGEAR_BOARD_ID NETGEAR_HW_ID
define Build/SenaoFW
-$(STAGING_DIR_HOST)/bin/mksenaofw \
-n $(BOARD_NAME) -r $(VENDOR_ID) -p $(1) \
-c $(DATECODE) -w $(2) -x $(CW_VER) -t 0 \
-e $@ \
-o $@.new
@cp $@.new $@
endef
define Device/8dev_jalapeno define Device/8dev_jalapeno
$(call Device/FitImage) $(call Device/FitImage)
@ -168,17 +176,27 @@ endef
TARGET_DEVICES += engenius_eap1300 TARGET_DEVICES += engenius_eap1300
define Device/engenius_ens620ext define Device/engenius_ens620ext
$(call Device/FitImage) $(call Device/FitImage)
DEVICE_DTS := qcom-ipq4018-ens620ext DEVICE_DTS := qcom-ipq4018-ens620ext
DEVICE_DTS_CONFIG := config@4 DEVICE_DTS_CONFIG := config@4
BLOCKSIZE := 64k BLOCKSIZE := 64k
PAGESIZE := 256 PAGESIZE := 256
DEVICE_TITLE := EnGenius ENS620EXT DEVICE_TITLE := EnGenius ENS620EXT
IMAGE_SIZE := 21823488 BOARD_NAME := ENS620EXT
KERNEL_SIZE := 5120k VENDOR_ID := 0x0101
FILESYSTEMS := squashfs PRODUCT_ID := 0x79
IMAGES := sysupgrade.bin PRODUCT_ID_NEW := 0xA4
IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | append-metadata DATECODE := 190507
FW_VER := 3.1.2
FW_VER_NEW := 3.5.6
CW_VER := 1.8.99
IMAGE_SIZE := 21823488
KERNEL_SIZE := 5120k
FILESYSTEMS := squashfs
IMAGES := sysupgrade.bin factory_30.bin factory_35.bin
IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | append-metadata
IMAGE/factory_30.bin := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | SenaoFW $$$$(PRODUCT_ID) $$$$(FW_VER)
IMAGE/factory_35.bin := qsdk-ipq-factory-nor | check-size $$$$(IMAGE_SIZE) | SenaoFW $$$$(PRODUCT_ID_NEW) $$$$(FW_VER_NEW)
DEVICE_PACKAGES := ipq-wifi-engenius_ens620ext DEVICE_PACKAGES := ipq-wifi-engenius_ens620ext
endef endef
TARGET_DEVICES += engenius_ens620ext TARGET_DEVICES += engenius_ens620ext

View File

@ -23,7 +23,7 @@ define Host/Compile
$(call cc,otrx) $(call cc,otrx)
$(call cc,motorola-bin) $(call cc,motorola-bin)
$(call cc,dgfirmware) $(call cc,dgfirmware)
$(call cc,mksenaofw md5) $(call cc,mksenaofw md5, -Wall --std=gnu99)
$(call cc,trx2usr) $(call cc,trx2usr)
$(call cc,ptgen) $(call cc,ptgen)
$(call cc,srec2bin) $(call cc,srec2bin)

View File

@ -55,6 +55,7 @@ typedef enum {
} op_mode; } op_mode;
static firmware_type FIRMWARE_TYPES[] = { static firmware_type FIRMWARE_TYPES[] = {
{ 0x00, "combo" }, /* Used for new capwap-included style header */
{ 0x01, "bootloader" }, { 0x01, "bootloader" },
{ 0x02, "kernel" }, { 0x02, "kernel" },
{ 0x03, "kernelapp" }, { 0x03, "kernelapp" },
@ -70,6 +71,21 @@ static firmware_type FIRMWARE_TYPES[] = {
{ 0x0c, "langpack (D-Link)" } { 0x0c, "langpack (D-Link)" }
}; };
#define MOD_DEFAULT 0x616C6C00
#define SKU_DEFAULT 0x0
#define DATECODE_NONE 0xFFFFFFFF
#define FIRMWARE_TYPE_NONE 0xFF
struct capwap_header {
uint32_t mod;
uint32_t sku;
uint32_t firmware_ver[3];
uint32_t datecode;
uint32_t capwap_ver[3];
uint32_t model_size;
uint8_t model[];
};
static long get_file_size(const char *filename) static long get_file_size(const char *filename)
{ {
FILE *fp_file; FILE *fp_file;
@ -84,15 +100,14 @@ static long get_file_size(const char *filename)
return result; return result;
} }
static int header_checksum(void *data, int len) static int header_checksum(void *data, size_t len)
{ {
int i; int sum = 0; /* shouldn't this be unsigned ? */
int sum; size_t i;
sum = 0; if (data != NULL && len > 0) {
if (data != NULL && len >= 0) {
for (i = 0; i < len; ++i) for (i = 0; i < len; ++i)
sum += *(unsigned char *) (data + i); sum += ((unsigned char *)data)[i];
return sum; return sum;
} }
@ -124,18 +139,20 @@ static int md5_file(const char *filename, uint8_t *dst)
} }
static int encode_image(const char *input_file_name, static int encode_image(const char *input_file_name,
const char *output_file_name, img_header *header, int block_size) const char *output_file_name, img_header *header,
struct capwap_header *cw_header, int block_size)
{ {
char buf[BUF_SIZE]; char buf[BUF_SIZE];
size_t bytes_read;
size_t pad_len = 0; size_t pad_len = 0;
size_t bytes_avail; size_t bytes_avail;
size_t bytes_read;
FILE *fp_input;
FILE *fp_output; FILE *fp_output;
FILE *fp_input;
int i; int model_size;
long magic; long magic;
size_t i;
fp_input = fopen(input_file_name, "r+b"); fp_input = fopen(input_file_name, "r+b");
if (!fp_input) { if (!fp_input) {
@ -172,6 +189,11 @@ static int encode_image(const char *input_file_name,
} }
header->zero = 0; header->zero = 0;
header->chksum = header_checksum(header, HDR_LEN); header->chksum = header_checksum(header, HDR_LEN);
if (cw_header) {
header->chksum += header_checksum(cw_header,
sizeof(struct capwap_header) + cw_header->model_size);
}
header->head = htonl(header->head); header->head = htonl(header->head);
header->vendor_id = htonl(header->vendor_id); header->vendor_id = htonl(header->vendor_id);
header->product_id = htonl(header->product_id); header->product_id = htonl(header->product_id);
@ -183,6 +205,22 @@ static int encode_image(const char *input_file_name,
fwrite(header, HDR_LEN, 1, fp_output); fwrite(header, HDR_LEN, 1, fp_output);
if (cw_header) {
model_size = cw_header->model_size;
cw_header->mod = htonl(cw_header->mod);
cw_header->sku = htonl(cw_header->sku);
cw_header->firmware_ver[0] = htonl(cw_header->firmware_ver[0]);
cw_header->firmware_ver[1] = htonl(cw_header->firmware_ver[1]);
cw_header->firmware_ver[2] = htonl(cw_header->firmware_ver[2]);
cw_header->datecode = htonl(cw_header->datecode);
cw_header->capwap_ver[0] = htonl(cw_header->capwap_ver[0]);
cw_header->capwap_ver[1] = htonl(cw_header->capwap_ver[1]);
cw_header->capwap_ver[2] = htonl(cw_header->capwap_ver[2]);
cw_header->model_size = htonl(cw_header->model_size);
fwrite(cw_header, sizeof(struct capwap_header) + model_size, 1,
fp_output);
}
while (!feof(fp_input) || pad_len > 0) { while (!feof(fp_input) || pad_len > 0) {
if (!feof(fp_input)) if (!feof(fp_input))
@ -212,32 +250,33 @@ static int encode_image(const char *input_file_name,
int decode_image(const char *input_file_name, const char *output_file_name) int decode_image(const char *input_file_name, const char *output_file_name)
{ {
img_header header; struct capwap_header cw_header;
char buf[BUF_SIZE]; char buf[BUF_SIZE];
img_header header;
char *pmodel = NULL;
FILE *fp_input; FILE *fp_input;
FILE *fp_output; FILE *fp_output;
unsigned int i;
size_t bytes_read; size_t bytes_read;
size_t bytes_written; size_t bytes_written;
unsigned int i;
fp_input = fopen(input_file_name, "r+b"); fp_input = fopen(input_file_name, "r+b");
if (!fp_input) { if (!fp_input) {
fprintf(stderr, "Cannot open %s !!\n", input_file_name); fprintf(stderr, "Cannot open %s !!\n", input_file_name);
fclose(fp_input);
return -1; return -1;
} }
fp_output = fopen(output_file_name, "w+b"); fp_output = fopen(output_file_name, "w+b");
if (!fp_output) { if (!fp_output) {
fprintf(stderr, "Cannot open %s !!\n", output_file_name); fprintf(stderr, "Cannot open %s !!\n", output_file_name);
fclose(fp_output); fclose(fp_input);
return -1; return -1;
} }
if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) { if (fread(&header, 1, HDR_LEN, fp_input) != HDR_LEN) {
fprintf(stderr, "Incorrect header size!!"); fprintf(stderr, "Incorrect header size reading base header!!");
fclose(fp_input); fclose(fp_input);
fclose(fp_output); fclose(fp_output);
return -1; return -1;
@ -251,6 +290,44 @@ int decode_image(const char *input_file_name, const char *output_file_name)
header.chksum = ntohl(header.chksum); header.chksum = ntohl(header.chksum);
header.magic = ntohl(header.magic); header.magic = ntohl(header.magic);
/* read capwap header if firmware_type is zero */
if (header.firmware_type == 0) {
if (fread(&cw_header, 1, sizeof(struct capwap_header),
fp_input) != sizeof(struct capwap_header)) {
fprintf(stderr, "Incorrect header size reading capwap_header!!");
fclose(fp_input);
fclose(fp_output);
return -1;
}
cw_header.mod = ntohl(cw_header.mod);
cw_header.sku = ntohl(cw_header.sku);
cw_header.firmware_ver[0] = ntohl(cw_header.firmware_ver[0]);
cw_header.firmware_ver[1] = ntohl(cw_header.firmware_ver[1]);
cw_header.firmware_ver[2] = ntohl(cw_header.firmware_ver[2]);
cw_header.datecode = ntohl(cw_header.datecode);
cw_header.capwap_ver[0] = ntohl(cw_header.capwap_ver[0]);
cw_header.capwap_ver[1] = ntohl(cw_header.capwap_ver[1]);
cw_header.capwap_ver[2] = ntohl(cw_header.capwap_ver[2]);
cw_header.model_size = ntohl(cw_header.model_size);
pmodel = malloc(cw_header.model_size + 1);
if (pmodel) {
pmodel[cw_header.model_size] = '\0';
if (fread(pmodel, 1, cw_header.model_size, fp_input) !=
cw_header.model_size) {
fprintf(stderr, "Incorrect header size reading model name!!");
fclose(fp_input);
fclose(fp_output);
return -1;
}
} else {
fprintf(stderr, "Incorrect header size reading model name!!");
fclose(fp_input);
fclose(fp_output);
return -1;
}
}
bytes_written = 0; bytes_written = 0;
while (!feof(fp_input)) { while (!feof(fp_input)) {
@ -281,7 +358,7 @@ int decode_image(const char *input_file_name, const char *output_file_name)
static void usage(const char *progname, int status) static void usage(const char *progname, int status)
{ {
FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout; FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
int i; size_t i;
fprintf(stream, "Usage: %s [OPTIONS...]\n", progname); fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
fprintf(stream, "\n" fprintf(stream, "\n"
@ -301,28 +378,47 @@ static void usage(const char *progname, int status)
" -m <magic> set encoding magic <magic>\n" " -m <magic> set encoding magic <magic>\n"
" -z enable image padding to <blocksize>\n" " -z enable image padding to <blocksize>\n"
" -b <blocksize> set image <blocksize>, defaults to %u\n" " -b <blocksize> set image <blocksize>, defaults to %u\n"
" -c <datecode> add capwap header with <datecode> (e.g. 171101)\n"
" -w <fw_ver> firmware version for capwap header (e.g. 3.0.1)\n"
" -x <cw_ver> capwap firmware version for capwap header (e.g. 1.8.53)\n"
" -n <name> model name for capwap header (e.g. ENS620EXT)\n"
" -h show this screen\n", DEFAULT_BLOCK_SIZE); " -h show this screen\n", DEFAULT_BLOCK_SIZE);
exit(status); exit(status);
} }
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int opt; static const char period[2] = ".";
char *input_file, *output_file, *progname = NULL; struct capwap_header cw_header;
op_mode mode = NONE;
int tmp, i, pad = 0;
int block_size;
img_header header; img_header header;
struct capwap_header *pcw_header = NULL;
char *output_file = NULL;
char *input_file = NULL;
char *progname = NULL;
char *mod_name = NULL;
char *token;
op_mode mode = NONE;
int tmp, pad = 0;
int block_size;
size_t i;
int opt;
block_size = DEFAULT_BLOCK_SIZE; block_size = DEFAULT_BLOCK_SIZE;
progname = basename(argv[0]); progname = basename(argv[0]);
memset(&header, 0, sizeof( img_header )); memset(&header, 0, sizeof(img_header));
header.magic = DEFAULT_MAGIC; header.magic = DEFAULT_MAGIC;
header.head = DEFAULT_HEAD_VALUE; header.head = DEFAULT_HEAD_VALUE;
header.firmware_type = FIRMWARE_TYPE_NONE;
memset(&cw_header, 0, sizeof(struct capwap_header));
cw_header.mod = MOD_DEFAULT;
cw_header.sku = SKU_DEFAULT;
cw_header.datecode = DATECODE_NONE;
strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1); strncpy( (char*)&header.version, DEFAULT_VERSION, VERSION_SIZE - 1);
while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:h?z")) != -1) { while ((opt = getopt(argc, argv, ":o:e:d:t:v:r:p:m:b:c:w:x:n:h?z")) != -1) {
switch (opt) { switch (opt) {
case 'e': case 'e':
input_file = optarg; input_file = optarg;
@ -344,7 +440,7 @@ int main(int argc, char *argv[])
break; break;
} }
} }
if (header.firmware_type == 0) { if (header.firmware_type == FIRMWARE_TYPE_NONE) {
fprintf(stderr, "Invalid firmware type \"0\"!\n"); fprintf(stderr, "Invalid firmware type \"0\"!\n");
usage(progname, EXIT_FAILURE); usage(progname, EXIT_FAILURE);
} }
@ -368,6 +464,31 @@ int main(int argc, char *argv[])
case 'b': case 'b':
block_size = strtol(optarg, 0, 10); block_size = strtol(optarg, 0, 10);
break; break;
case 'c':
cw_header.datecode = strtoul(optarg, 0, 10);
break;
case 'w':
token = strtok(optarg, period);
i = 0;
while (token && (i < 3)) {
cw_header.firmware_ver[i++] =
strtoul(token, 0, 10);
token = strtok(NULL, period);
}
break;
case 'x':
token = strtok(optarg, period);
i = 0;
while (token && (i < 3)) {
cw_header.capwap_ver[i++] =
strtoul(token, 0, 10);
token = strtok(NULL, period);
}
break;
case 'n':
mod_name = optarg;
cw_header.model_size = strlen(mod_name);
break;
case 'h': case 'h':
usage(progname, EXIT_SUCCESS); usage(progname, EXIT_SUCCESS);
break; break;
@ -403,17 +524,46 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
if (header.firmware_type == 0) { if ((header.firmware_type == 0) &&
fprintf(stderr, "Firmware type must be defined\n"); (cw_header.datecode == DATECODE_NONE)) {
fprintf(stderr, "Firmware type must be non-zero for non-capwap images\n");
usage(progname, EXIT_FAILURE); usage(progname, EXIT_FAILURE);
} }
if (header.vendor_id == 0 || header.product_id == 0) { if (header.vendor_id == 0 || header.product_id == 0) {
fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n"); fprintf(stderr, "Vendor ID and Product ID must be defined and non-zero\n");
usage(progname, EXIT_FAILURE); usage(progname, EXIT_FAILURE);
} }
if (encode_image(input_file, output_file, &header, pad ? block_size : 0) < 0) /* Check capwap header specific arguments */
if (cw_header.datecode != DATECODE_NONE) {
if (!mod_name) {
fprintf(stderr, "Capwap header specified: model name must be specified\n");
usage(progname, EXIT_FAILURE);
}
if (!cw_header.firmware_ver[0] && !cw_header.firmware_ver[1] &&
!cw_header.firmware_ver[2]) {
fprintf(stderr, "Capwap header specified, fw_ver must be non-zero\n");
}
if (!cw_header.capwap_ver[0] && !cw_header.capwap_ver[1] &&
!cw_header.capwap_ver[2]) {
fprintf(stderr, "Capwap header specified, cw_ver must be non-zero\n");
}
pcw_header = malloc(sizeof(struct capwap_header) +
cw_header.model_size);
if (pcw_header) {
memcpy(pcw_header, &cw_header,
sizeof(struct capwap_header));
memcpy(&(pcw_header->model), mod_name,
cw_header.model_size);
} else {
fprintf(stderr, "Failed to allocate memory\n");
return EXIT_FAILURE;
}
}
if (encode_image(input_file, output_file, &header, pcw_header,
pad ? block_size : 0) < 0)
return EXIT_FAILURE; return EXIT_FAILURE;
return EXIT_SUCCESS; return EXIT_SUCCESS;