ipq40xx: add factory image for EnGenius ENS620EXT
authorSteve Glennon <s.glennon@cablelabs.com>
Tue, 9 Apr 2019 20:46:32 +0000 (14:46 -0600)
committerChristian Lamparter <chunkeey@gmail.com>
Sat, 11 May 2019 14:37:11 +0000 (16:37 +0200)
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.]

target/linux/ipq40xx/image/Makefile
tools/firmware-utils/Makefile
tools/firmware-utils/src/mksenaofw.c

index 626223a..ae146cd 100644 (file)
@@ -51,6 +51,14 @@ define Device/DniImage
 endef
 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
        $(call Device/FitImage)
@@ -168,17 +176,27 @@ endef
 TARGET_DEVICES += engenius_eap1300
 
 define Device/engenius_ens620ext
-        $(call Device/FitImage)
-        DEVICE_DTS := qcom-ipq4018-ens620ext
-        DEVICE_DTS_CONFIG := config@4
-        BLOCKSIZE := 64k
-        PAGESIZE := 256
-        DEVICE_TITLE := EnGenius ENS620EXT
-        IMAGE_SIZE := 21823488
-        KERNEL_SIZE := 5120k
-        FILESYSTEMS := squashfs
-        IMAGES := sysupgrade.bin
-        IMAGE/sysupgrade.bin := append-kernel | append-rootfs | pad-rootfs | check-size $$$$(IMAGE_SIZE) | append-metadata
+       $(call Device/FitImage)
+       DEVICE_DTS := qcom-ipq4018-ens620ext
+       DEVICE_DTS_CONFIG := config@4
+       BLOCKSIZE := 64k
+       PAGESIZE := 256
+       DEVICE_TITLE := EnGenius ENS620EXT
+       BOARD_NAME := ENS620EXT
+       VENDOR_ID := 0x0101
+       PRODUCT_ID := 0x79
+       PRODUCT_ID_NEW := 0xA4
+       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
 endef
 TARGET_DEVICES += engenius_ens620ext
index 76d5929..bde90f0 100644 (file)
@@ -23,7 +23,7 @@ define Host/Compile
        $(call cc,otrx)
        $(call cc,motorola-bin)
        $(call cc,dgfirmware)
-       $(call cc,mksenaofw md5)
+       $(call cc,mksenaofw md5, -Wall --std=gnu99)
        $(call cc,trx2usr)
        $(call cc,ptgen)
        $(call cc,srec2bin)
index 0f10ebd..b0fb720 100644 (file)
@@ -55,6 +55,7 @@ typedef enum {
 } op_mode;
 
 static firmware_type FIRMWARE_TYPES[] = {
+       { 0x00, "combo" }, /* Used for new capwap-included style header */
        { 0x01, "bootloader" },
        { 0x02, "kernel" },
        { 0x03, "kernelapp" },
@@ -70,6 +71,21 @@ static firmware_type FIRMWARE_TYPES[] = {
        { 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)
 {
        FILE *fp_file;
@@ -84,15 +100,14 @@ static long get_file_size(const char *filename)
        return result;
 }
 
-static int header_checksum(void *data, int len)
+static int header_checksum(void *data, size_t len)
 {
-       int i;
-       int sum;
+       int sum = 0; /* shouldn't this be unsigned ? */
+       size_t i;
 
-       sum = 0;
-       if (data != NULL && len >= 0) {
+       if (data != NULL && len > 0) {
                for (i = 0; i < len; ++i)
-                       sum += *(unsigned char *) (data + i);
+                       sum += ((unsigned char *)data)[i];
                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,
-               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];
-       size_t bytes_read;
        size_t pad_len = 0;
        size_t bytes_avail;
+       size_t bytes_read;
 
-       FILE *fp_input;
        FILE *fp_output;
+       FILE *fp_input;
 
-       int i;
+       int model_size;
        long magic;
+       size_t i;
 
        fp_input = fopen(input_file_name, "r+b");
        if (!fp_input) {
@@ -172,6 +189,11 @@ static int encode_image(const char *input_file_name,
        }
        header->zero = 0;
        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->vendor_id = htonl(header->vendor_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);
 
+       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) {
 
                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)
 {
-       img_header header;
+       struct capwap_header cw_header;
        char buf[BUF_SIZE];
+       img_header header;
 
+       char *pmodel = NULL;
        FILE *fp_input;
        FILE *fp_output;
-       unsigned int i;
 
        size_t bytes_read;
        size_t bytes_written;
+       unsigned int i;
 
        fp_input = fopen(input_file_name, "r+b");
        if (!fp_input) {
                fprintf(stderr, "Cannot open %s !!\n", input_file_name);
-               fclose(fp_input);
                return -1;
        }
 
        fp_output = fopen(output_file_name, "w+b");
        if (!fp_output) {
                fprintf(stderr, "Cannot open %s !!\n", output_file_name);
-               fclose(fp_output);
+               fclose(fp_input);
                return -1;
        }
 
        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_output);
                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.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;
        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)
 {
        FILE *stream = (status != EXIT_SUCCESS) ? stderr : stdout;
-       int i;
+       size_t i;
 
        fprintf(stream, "Usage: %s [OPTIONS...]\n", progname);
        fprintf(stream, "\n"
@@ -301,28 +378,47 @@ static void usage(const char *progname, int status)
                        "  -m <magic>           set encoding magic <magic>\n"
                        "  -z                   enable image padding to <blocksize>\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);
        exit(status);
 }
 
 int main(int argc, char *argv[])
 {
-       int opt;
-       char *input_file, *output_file, *progname = NULL;
+       static const char period[2] = ".";
+       struct capwap_header cw_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, i, pad = 0;
+       int tmp, pad = 0;
        int block_size;
-       img_header header;
+       size_t i;
+       int opt;
 
        block_size = DEFAULT_BLOCK_SIZE;
        progname = basename(argv[0]);
 
-       memset(&header, 0, sizeof( img_header ));
+       memset(&header, 0, sizeof(img_header));
        header.magic = DEFAULT_MAGIC;
        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);
 
-       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) {
                case 'e':
                        input_file = optarg;
@@ -344,7 +440,7 @@ int main(int argc, char *argv[])
                                        break;
                                }
                        }
-                       if (header.firmware_type == 0) {
+                       if (header.firmware_type == FIRMWARE_TYPE_NONE) {
                                fprintf(stderr, "Invalid firmware type \"0\"!\n");
                                usage(progname, EXIT_FAILURE);
                        }
@@ -368,6 +464,31 @@ int main(int argc, char *argv[])
                case 'b':
                        block_size = strtol(optarg, 0, 10);
                        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':
                        usage(progname, EXIT_SUCCESS);
                        break;
@@ -403,17 +524,46 @@ int main(int argc, char *argv[])
                return EXIT_SUCCESS;
        }
 
-       if (header.firmware_type == 0) {
-               fprintf(stderr, "Firmware type must be defined\n");
+       if ((header.firmware_type == 0) &&
+           (cw_header.datecode == DATECODE_NONE)) {
+               fprintf(stderr, "Firmware type must be non-zero for non-capwap images\n");
                usage(progname, EXIT_FAILURE);
        }
 
        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);
        }
 
-       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_SUCCESS;