Explorar o código

Add support for TP-LINK CPE210/220/510/520

Matthias Schiffer %!s(int64=9) %!d(string=hai) anos
pai
achega
ab729ed307

+ 570 - 0
patches/openwrt/0005-firmware-utils-add-new-tool-tplink-safeloader-for-the-new-TP-LINK-Pharos-devices-CPE210-220-510-520.patch

@@ -0,0 +1,570 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 26 Nov 2014 19:57:39 +0100
+Subject: firmware-utils: add new tool tplink-safeloader for the new TP-LINK Pharos devices (CPE210/220/510/520)
+
+The new TP-LINK Pharos series uses a new bootloader, the "TP-LINK Safeloader".
+It uses an advanced firmware image format, containing an image partition table
+and a flash partition table (and image partitions are mapped to the
+corresponding flash partitions). The exact image format is documented in the
+source code.
+
+Furthermore, the bootloader expects the kernel image as an ELF executable.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/tools/firmware-utils/Makefile b/tools/firmware-utils/Makefile
+index 4bb53cb..3f9eb56 100644
+--- a/tools/firmware-utils/Makefile
++++ b/tools/firmware-utils/Makefile
+@@ -41,6 +41,7 @@ define Host/Compile
+ 	$(call cc,mkplanexfw sha1)
+ 	$(call cc,mktplinkfw md5)
+ 	$(call cc,mktplinkfw2 md5)
++	$(call cc,tplink-safeloader md5, -Wall)
+ 	$(call cc,pc1crypt)
+ 	$(call cc,osbridge-crc)
+ 	$(call cc,wrt400n cyg_crc32)
+diff --git a/tools/firmware-utils/src/tplink-safeloader.c b/tools/firmware-utils/src/tplink-safeloader.c
+new file mode 100644
+index 0000000..23d703f
+--- /dev/null
++++ b/tools/firmware-utils/src/tplink-safeloader.c
+@@ -0,0 +1,538 @@
++/*
++  Copyright (c) 2014, Matthias Schiffer <mschiffer@universe-factory.net>
++  All rights reserved.
++
++  Redistribution and use in source and binary forms, with or without
++  modification, are permitted provided that the following conditions are met:
++
++    1. Redistributions of source code must retain the above copyright notice,
++       this list of conditions and the following disclaimer.
++    2. Redistributions in binary form must reproduce the above copyright notice,
++       this list of conditions and the following disclaimer in the documentation
++       and/or other materials provided with the distribution.
++
++  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
++  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
++  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
++  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
++  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
++  DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
++  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
++  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
++  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
++*/
++
++
++/*
++   tplink-safeloader
++
++   Image generation tool for the TP-LINK SafeLoader as seen on
++   TP-LINK Pharos devices (CPE210/220/510/520)
++*/
++
++
++#include <assert.h>
++#include <errno.h>
++#include <error.h>
++#include <stdbool.h>
++#include <stdio.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <string.h>
++#include <time.h>
++#include <unistd.h>
++
++#include <arpa/inet.h>
++
++#include <sys/types.h>
++#include <sys/stat.h>
++
++#include "md5.h"
++
++
++#define ALIGN(x,a) ({ typeof(a) __a = (a); (((x) + __a - 1) & ~(__a - 1)); })
++
++
++/** An image partition table entry */
++struct image_partition_entry {
++	const char *name;
++	size_t size;
++	uint8_t *data;
++};
++
++/** A flash partition table entry */
++struct flash_partition_entry {
++	const char *name;
++	uint32_t base;
++	uint32_t size;
++};
++
++
++/** The content of the soft-version structure */
++struct __attribute__((__packed__)) soft_version {
++	uint32_t magic;
++	uint32_t zero;
++	uint8_t pad1;
++	uint8_t version_major;
++	uint8_t version_minor;
++	uint8_t version_patch;
++	uint8_t year_hi;
++	uint8_t year_lo;
++	uint8_t month;
++	uint8_t day;
++	uint32_t rev;
++	uint8_t pad2;
++};
++
++
++static const uint8_t jffs2_eof_mark[4] = {0xde, 0xad, 0xc0, 0xde};
++
++
++/**
++   Salt for the MD5 hash
++
++   Fortunately, TP-LINK seems to use the same salt for most devices which use
++   the new image format.
++*/
++static const uint8_t md5_salt[16] = {
++	0x7a, 0x2b, 0x15, 0xed,
++	0x9b, 0x98, 0x59, 0x6d,
++	0xe5, 0x04, 0xab, 0x44,
++	0xac, 0x2a, 0x9f, 0x4e,
++};
++
++
++/** Vendor information for CPE210/220/510/520 */
++static const unsigned char cpe510_vendor[] = "\x00\x00\x00\x1f""CPE510(TP-LINK|UN|N300-5):1.0\r\n";
++
++
++/**
++    The flash partition table for CPE210/220/510/520;
++    it is the same as the one used by the stock images.
++*/
++static const struct flash_partition_entry cpe510_partitions[] = {
++	{"fs-uboot", 0x00000, 0x20000},
++	{"partition-table", 0x20000, 0x02000},
++	{"default-mac", 0x30000, 0x00020},
++	{"product-info", 0x31100, 0x00100},
++	{"signature", 0x32000, 0x00400},
++	{"os-image", 0x40000, 0x170000},
++	{"soft-version", 0x1b0000, 0x00100},
++	{"support-list", 0x1b1000, 0x00400},
++	{"file-system", 0x1c0000, 0x600000},
++	{"user-config", 0x7c0000, 0x10000},
++	{"default-config", 0x7d0000, 0x10000},
++	{"log", 0x7e0000, 0x10000},
++	{"radio", 0x7f0000, 0x10000},
++	{NULL, 0, 0}
++};
++
++/**
++   The support list for CPE210/220/510/520
++
++   The stock images also contain strings for two more devices: BS510 and BS210.
++   At the moment, there exists no public information about these devices.
++*/
++static const unsigned char cpe510_support_list[] =
++	"\x00\x00\x00\xc8\x00\x00\x00\x00"
++	"SupportList:\r\n"
++	"CPE510(TP-LINK|UN|N300-5):1.0\r\n"
++	"CPE520(TP-LINK|UN|N300-5):1.0\r\n"
++	"CPE210(TP-LINK|UN|N300-2):1.0\r\n"
++	"CPE220(TP-LINK|UN|N300-2):1.0\r\n"
++	"\r\n\xff";
++
++
++/** Allocates a new image partition */
++struct image_partition_entry alloc_image_partition(const char *name, size_t len) {
++	struct image_partition_entry entry = {name, len, malloc(len)};
++	if (!entry.data)
++		error(1, errno, "malloc");
++
++	return entry;
++}
++
++/** Frees an image partition */
++void free_image_partition(struct image_partition_entry entry) {
++	free(entry.data);
++}
++
++/** Generates the partition-table partition */
++struct image_partition_entry make_partition_table(const struct flash_partition_entry *p) {
++	struct image_partition_entry entry = alloc_image_partition("partition-table", 0x800);
++
++	char *s = (char *)entry.data, *end = (char *)(s+entry.size);
++
++	*(s++) = 0x00;
++	*(s++) = 0x04;
++	*(s++) = 0x00;
++	*(s++) = 0x00;
++
++	size_t i;
++	for (i = 0; p[i].name; i++) {
++		size_t len = end-s;
++		size_t w = snprintf(s, len, "partition %s base 0x%05x size 0x%05x\n", p[i].name, p[i].base, p[i].size);
++
++		if (w > len-1)
++			error(1, 0, "flash partition table overflow?");
++
++		s += w;
++	}
++
++	s++;
++
++	memset(s, 0xff, end-s);
++
++	return entry;
++}
++
++
++/** Generates a binary-coded decimal representation of an integer in the range [0, 99] */
++static inline uint8_t bcd(uint8_t v) {
++	return 0x10 * (v/10) + v%10;
++}
++
++
++/** Generates the soft-version partition */
++struct image_partition_entry make_soft_version(uint32_t rev) {
++	struct image_partition_entry entry = alloc_image_partition("soft-version", sizeof(struct soft_version));
++	struct soft_version *s = (struct soft_version *)entry.data;
++
++	time_t t;
++	if (time(&t) == (time_t)(-1))
++		error(1, errno, "time");
++
++	struct tm *tm = localtime(&t);
++
++	s->magic = htonl(0x0000000c);
++	s->zero = 0;
++	s->pad1 = 0xff;
++
++	s->version_major = 0;
++	s->version_minor = 0;
++	s->version_patch = 0;
++
++	s->year_hi = bcd((1900+tm->tm_year)/100);
++	s->year_lo = bcd(tm->tm_year%100);
++	s->month = bcd(tm->tm_mon+1);
++	s->day = bcd(tm->tm_mday);
++	s->rev = htonl(rev);
++
++	s->pad2 = 0xff;
++
++	return entry;
++}
++
++/** Generates the support-list partition */
++struct image_partition_entry make_support_list(const unsigned char *support_list, size_t len) {
++	struct image_partition_entry entry = alloc_image_partition("support-list", len);
++	memcpy(entry.data, support_list, len);
++	return entry;
++}
++
++/** Creates a new image partition with an arbitrary name from a file */
++struct image_partition_entry read_file(const char *part_name, const char *filename, bool add_jffs2_eof) {
++	struct stat statbuf;
++
++	if (stat(filename, &statbuf) < 0)
++		error(1, errno, "unable to stat file `%s'", filename);
++
++	size_t len = statbuf.st_size;
++
++	if (add_jffs2_eof)
++		len = ALIGN(len, 0x10000) + sizeof(jffs2_eof_mark);
++
++	struct image_partition_entry entry = alloc_image_partition(part_name, len);
++
++	FILE *file = fopen(filename, "rb");
++	if (!file)
++		error(1, errno, "unable to open file `%s'", filename);
++
++	if (fread(entry.data, statbuf.st_size, 1, file) != 1)
++		error(1, errno, "unable to read file `%s'", filename);
++
++	if (add_jffs2_eof) {
++		uint8_t *eof = entry.data + statbuf.st_size, *end = entry.data+entry.size;
++
++		memset(eof, 0xff, end - eof - sizeof(jffs2_eof_mark));
++		memcpy(end - sizeof(jffs2_eof_mark), jffs2_eof_mark, sizeof(jffs2_eof_mark));
++	}
++
++	fclose(file);
++
++	return entry;
++}
++
++
++/**
++   Copies a list of image partitions into an image buffer and generates the image partition table while doing so
++
++   Example image partition table:
++
++     fwup-ptn partition-table base 0x00800 size 0x00800
++     fwup-ptn os-image base 0x01000 size 0x113b45
++     fwup-ptn file-system base 0x114b45 size 0x1d0004
++     fwup-ptn support-list base 0x2e4b49 size 0x000d1
++
++   Each line of the partition table is terminated with the bytes 09 0d 0a ("\t\r\n"),
++   the end of the partition table is marked with a zero byte.
++
++   The firmware image must contain at least the partition-table and support-list partitions
++   to be accepted. There aren't any alignment constraints for the image partitions.
++
++   The partition-table partition contains the actual flash layout; partitions
++   from the image partition table are mapped to the corresponding flash partitions during
++   the firmware upgrade. The support-list partition contains a list of devices supported by
++   the firmware image.
++
++   The base offsets in the firmware partition table are relative to the end
++   of the vendor information block, so the partition-table partition will
++   actually start at offset 0x1814 of the image.
++
++   I think partition-table must be the first partition in the firmware image.
++*/
++void put_partitions(uint8_t *buffer, const struct image_partition_entry *parts) {
++	size_t i;
++	char *image_pt = (char *)buffer, *end = image_pt + 0x800;
++
++	size_t base = 0x800;
++	for (i = 0; parts[i].name; i++) {
++		memcpy(buffer + base, parts[i].data, parts[i].size);
++
++		size_t len = end-image_pt;
++		size_t w = snprintf(image_pt, len, "fwup-ptn %s base 0x%05x size 0x%05x\t\r\n", parts[i].name, (unsigned)base, (unsigned)parts[i].size);
++
++		if (w > len-1)
++			error(1, 0, "image partition table overflow?");
++
++		image_pt += w;
++
++		base += parts[i].size;
++	}
++
++	image_pt++;
++
++	memset(image_pt, 0xff, end-image_pt);
++}
++
++/** Generates and writes the image MD5 checksum */
++void put_md5(uint8_t *md5, uint8_t *buffer, unsigned int len) {
++	MD5_CTX ctx;
++
++	MD5_Init(&ctx);
++	MD5_Update(&ctx, md5_salt, (unsigned int)sizeof(md5_salt));
++	MD5_Update(&ctx, buffer, len);
++	MD5_Final(md5, &ctx);
++}
++
++
++/**
++   Generates the firmware image in factory format
++
++   Image format:
++
++     Bytes (hex)  Usage
++     -----------  -----
++     0000-0003    Image size (4 bytes, big endian)
++     0004-0013    MD5 hash (hash of a 16 byte salt and the image data starting with byte 0x14)
++     0014-1013    Vendor information (4096 bytes, padded with 0xff; there seem to be older
++                  (VxWorks-based) TP-LINK devices which use a smaller vendor information block)
++     1014-1813    Image partition table (2048 bytes, padded with 0xff)
++     1814-xxxx    Firmware partitions
++*/
++void * generate_factory_image(const unsigned char *vendor, size_t vendor_len, const struct image_partition_entry *parts, size_t *len) {
++	*len = 0x1814;
++
++	size_t i;
++	for (i = 0; parts[i].name; i++)
++		*len += parts[i].size;
++
++	uint8_t *image = malloc(*len);
++	if (!image)
++		error(1, errno, "malloc");
++
++	image[0] = *len >> 24;
++	image[1] = *len >> 16;
++	image[2] = *len >> 8;
++	image[3] = *len;
++
++	memcpy(image+0x14, vendor, vendor_len);
++	memset(image+0x14+vendor_len, 0xff, 4096-vendor_len);
++
++	put_partitions(image + 0x1014, parts);
++	put_md5(image+0x04, image+0x14, *len-0x14);
++
++	return image;
++}
++
++/**
++   Generates the firmware image in sysupgrade format
++
++   This makes some assumptions about the provided flash and image partition tables and
++   should be generalized when TP-LINK starts building its safeloader into hardware with
++   different flash layouts.
++*/
++void * generate_sysupgrade_image(const struct flash_partition_entry *flash_parts, const struct image_partition_entry *image_parts, size_t *len) {
++	const struct flash_partition_entry *flash_os_image = &flash_parts[5];
++	const struct flash_partition_entry *flash_soft_version = &flash_parts[6];
++	const struct flash_partition_entry *flash_support_list = &flash_parts[7];
++	const struct flash_partition_entry *flash_file_system = &flash_parts[8];
++
++	const struct image_partition_entry *image_os_image = &image_parts[3];
++	const struct image_partition_entry *image_soft_version = &image_parts[1];
++	const struct image_partition_entry *image_support_list = &image_parts[2];
++	const struct image_partition_entry *image_file_system = &image_parts[4];
++
++	assert(strcmp(flash_os_image->name, "os-image") == 0);
++	assert(strcmp(flash_soft_version->name, "soft-version") == 0);
++	assert(strcmp(flash_support_list->name, "support-list") == 0);
++	assert(strcmp(flash_file_system->name, "file-system") == 0);
++
++	assert(strcmp(image_os_image->name, "os-image") == 0);
++	assert(strcmp(image_soft_version->name, "soft-version") == 0);
++	assert(strcmp(image_support_list->name, "support-list") == 0);
++	assert(strcmp(image_file_system->name, "file-system") == 0);
++
++	if (image_os_image->size > flash_os_image->size)
++		error(1, 0, "kernel image too big (more than %u bytes)", (unsigned)flash_os_image->size);
++	if (image_file_system->size > flash_file_system->size)
++		error(1, 0, "rootfs image too big (more than %u bytes)", (unsigned)flash_file_system->size);
++
++	*len = flash_file_system->base - flash_os_image->base + image_file_system->size;
++
++	uint8_t *image = malloc(*len);
++	if (!image)
++		error(1, errno, "malloc");
++
++	memset(image, 0xff, *len);
++
++	memcpy(image, image_os_image->data, image_os_image->size);
++	memcpy(image + flash_soft_version->base - flash_os_image->base, image_soft_version->data, image_soft_version->size);
++	memcpy(image + flash_support_list->base - flash_os_image->base, image_support_list->data, image_support_list->size);
++	memcpy(image + flash_file_system->base - flash_os_image->base, image_file_system->data, image_file_system->size);
++
++	return image;
++}
++
++
++/** Generates an image for CPE210/220/510/520 and writes it to a file */
++static void do_cpe510(const char *output, const char *kernel_image, const char *rootfs_image, uint32_t rev, bool add_jffs2_eof, bool sysupgrade) {
++	struct image_partition_entry parts[6] = {};
++
++	parts[0] = make_partition_table(cpe510_partitions);
++	parts[1] = make_soft_version(rev);
++	parts[2] = make_support_list(cpe510_support_list, sizeof(cpe510_support_list)-1);
++	parts[3] = read_file("os-image", kernel_image, false);
++	parts[4] = read_file("file-system", rootfs_image, add_jffs2_eof);
++
++	size_t len;
++	void *image;
++	if (sysupgrade)
++		image = generate_sysupgrade_image(cpe510_partitions, parts, &len);
++	else
++		image = generate_factory_image(cpe510_vendor, sizeof(cpe510_vendor)-1, parts, &len);
++
++	FILE *file = fopen(output, "wb");
++	if (!file)
++		error(1, errno, "unable to open output file");
++
++	if (fwrite(image, len, 1, file) != 1)
++		error(1, 0, "unable to write output file");
++
++	fclose(file);
++
++	free(image);
++
++	size_t i;
++	for (i = 0; parts[i].name; i++)
++		free_image_partition(parts[i]);
++}
++
++
++/** Usage output */
++void usage(const char *argv0) {
++	fprintf(stderr,
++		"Usage: %s [OPTIONS...]\n"
++		"\n"
++		"Options:\n"
++		"  -B <board>      create image for the board specified with <board>\n"
++		"  -k <file>       read kernel image from the file <file>\n"
++		"  -r <file>       read rootfs image from the file <file>\n"
++		"  -o <file>       write output to the file <file>\n"
++		"  -V <rev>        sets the revision number to <rev>\n"
++		"  -j              add jffs2 end-of-filesystem markers\n"
++		"  -S              create sysupgrade instead of factory image\n"
++		"  -h              show this help\n",
++		argv0
++	);
++};
++
++
++int main(int argc, char *argv[]) {
++	const char *board = NULL, *kernel_image = NULL, *rootfs_image = NULL, *output = NULL;
++	bool add_jffs2_eof = false, sysupgrade = false;
++	unsigned rev = 0;
++
++	while (true) {
++		int c;
++
++		c = getopt(argc, argv, "B:k:r:o:V:jSh");
++		if (c == -1)
++			break;
++
++		switch (c) {
++		case 'B':
++			board = optarg;
++			break;
++
++		case 'k':
++			kernel_image = optarg;
++			break;
++
++		case 'r':
++			rootfs_image = optarg;
++			break;
++
++		case 'o':
++			output = optarg;
++			break;
++
++		case 'V':
++			sscanf(optarg, "r%u", &rev);
++			break;
++
++		case 'j':
++			add_jffs2_eof = true;
++			break;
++
++		case 'S':
++			sysupgrade = true;
++			break;
++
++		case 'h':
++			usage(argv[0]);
++			return 0;
++
++		default:
++			usage(argv[0]);
++			return 1;
++		}
++	}
++
++	if (!board)
++		error(1, 0, "no board has been specified");
++	if (!kernel_image)
++		error(1, 0, "no kernel image has been specified");
++	if (!rootfs_image)
++		error(1, 0, "no rootfs image has been specified");
++	if (!output)
++		error(1, 0, "no output filename has been specified");
++
++	if (strcmp(board, "CPE510") == 0)
++		do_cpe510(output, kernel_image, rootfs_image, rev, add_jffs2_eof, sysupgrade);
++	else
++		error(1, 0, "unsupported board %s", board);
++
++	return 0;
++}

+ 471 - 0
patches/openwrt/0006-ar71xx-add-support-for-TP-LINK-CPE210-220-510-520.patch

@@ -0,0 +1,471 @@
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Wed, 26 Nov 2014 19:57:50 +0100
+Subject: ar71xx: add support for TP-LINK CPE210/220/510/520
+
+This adds support for the TP-LINK CPE210/220/510/520 (Pharos series). These
+devices are very similar to the Ubiquiti NanoStations, but with better specs:
+faster CPU, more RAM, 2x2 MIMO.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+
+diff --git a/target/linux/ar71xx/base-files/etc/diag.sh b/target/linux/ar71xx/base-files/etc/diag.sh
+index 1864b11..4b510dd 100755
+--- a/target/linux/ar71xx/base-files/etc/diag.sh
++++ b/target/linux/ar71xx/base-files/etc/diag.sh
+@@ -43,6 +43,9 @@ get_status_led() {
+ 	cap4200ag)
+ 		status_led="senao:green:pwr"
+ 		;;
++	cpe510)
++		status_led="tp-link:green:link4"
++		;;
+ 	db120)
+ 		status_led="db120:green:status"
+ 		;;
+diff --git a/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds b/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds
+index d3b766d..fb8df40 100755
+--- a/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds
++++ b/target/linux/ar71xx/base-files/etc/uci-defaults/01_leds
+@@ -61,6 +61,16 @@ carambola2)
+ 	ucidef_set_led_wlan "wlan" "WLAN" "carambola2:green:wlan" "phy0tpt"
+ 	;;
+ 
++cpe510)
++	ucidef_set_led_switch "lan0" "LAN0" "tp-link:green:lan0" "switch0" "0x20"
++	ucidef_set_led_switch "lan1" "LAN1" "tp-link:green:lan1" "switch0" "0x10"
++	ucidef_set_rssimon "wlan0" "40000" "1"
++	ucidef_set_led_rssi "rssilow" "RSSILOW" "tp-link:green:link1" "wlan0" "1" "100" "0" "13"
++	ucidef_set_led_rssi "rssimediumlow" "RSSIMEDIUMLOW" "tp-link:green:link2" "wlan0" "26" "100" "-25" "13"
++	ucidef_set_led_rssi "rssimediumhigh" "RSSIMEDIUMHIGH" "tp-link:green:link3" "wlan0" "51" "100" "-50" "13"
++	ucidef_set_led_rssi "rssihigh" "RSSIHIGH" "tp-link:green:link4" "wlan0" "76" "100" "-75" "13"
++	;;
++
+ db120)
+ 	ucidef_set_led_usbdev "usb" "USB" "db120:green:usb" "1-1"
+ 	;;
+diff --git a/target/linux/ar71xx/base-files/etc/uci-defaults/02_network b/target/linux/ar71xx/base-files/etc/uci-defaults/02_network
+index c5cfd67..a9f00fa 100755
+--- a/target/linux/ar71xx/base-files/etc/uci-defaults/02_network
++++ b/target/linux/ar71xx/base-files/etc/uci-defaults/02_network
+@@ -62,6 +62,13 @@ tl-wdr4900-v2)
+ 	ucidef_add_switch_vlan "switch0" "2" "1 6"
+ 	;;
+ 
++cpe510)
++	ucidef_set_interfaces_lan_wan "eth0.1" "eth0.2"
++	ucidef_add_switch "switch0" "1" "1"
++	ucidef_add_switch_vlan "switch0" "1" "0t 5"
++	ucidef_add_switch_vlan "switch0" "2" "0t 4"
++	;;
++
+ db120 |\
+ rb-2011l | \
+ rb-2011uas |\
+diff --git a/target/linux/ar71xx/base-files/lib/ar71xx.sh b/target/linux/ar71xx/base-files/lib/ar71xx.sh
+index 5aceaee..0b0a8d6 100755
+--- a/target/linux/ar71xx/base-files/lib/ar71xx.sh
++++ b/target/linux/ar71xx/base-files/lib/ar71xx.sh
+@@ -229,6 +229,39 @@ tplink_board_detect() {
+ 	AR71XX_MODEL="$model $hwver"
+ }
+ 
++tplink_pharos_get_model_string() {
++	local part
++	part=$(find_mtd_part 'product-info')
++	[ -z "$part" ] && return 1
++
++	# The returned string will end with \r\n, but we don't remove it here
++	# to simplify matching against it in the sysupgrade image check
++	dd if=$part bs=1 skip=4360 2>/dev/null | head -n 1
++}
++
++tplink_pharos_board_detect() {
++	local model_string="$(tplink_pharos_get_model_string | tr -d '\r')"
++	local oIFS="$IFS"; IFS=":"; set -- $model_string; IFS="$oIFS"
++	local model
++
++	case "$1" in
++	'CPE210(TP-LINK|UN|N300-2)')
++		model='TP-Link CPE210'
++		;;
++	'CPE220(TP-LINK|UN|N300-2)')
++		model='TP-Link CPE220'
++		;;
++	'CPE510(TP-LINK|UN|N300-5)')
++		model='TP-Link CPE510'
++		;;
++	'CPE520(TP-LINK|UN|N300-5)')
++		model='TP-Link CPE520'
++		;;
++	esac
++
++	[ -n "$model" ] && AR71XX_MODEL="$model v$2"
++}
++
+ ar71xx_board_detect() {
+ 	local machine
+ 	local name
+@@ -302,6 +335,10 @@ ar71xx_board_detect() {
+ 	*CAP4200AG)
+ 		name="cap4200ag"
+ 		;;
++	*"CPE210/220/510/520")
++		name="cpe510"
++		tplink_pharos_board_detect
++		;;
+ 	*"DB120 reference board")
+ 		name="db120"
+ 		;;
+@@ -751,11 +788,8 @@ ar71xx_board_detect() {
+ 		;;
+ 	esac
+ 
+-	case "$machine" in
+-	*TL-WR* | *TL-WA* | *TL-MR* | *TL-WD* | *Archer*)
++	[ -z "$AR71XX_MODEL" ] && [ "${machine:0:8}" = 'TP-LINK ' ] && \
+ 		tplink_board_detect "$machine"
+-		;;
+-	esac
+ 
+ 	[ -z "$name" ] && name="unknown"
+ 
+diff --git a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
+index 846954c..15e998c 100755
+--- a/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
++++ b/target/linux/ar71xx/base-files/lib/upgrade/platform.sh
+@@ -70,6 +70,33 @@ tplink_get_image_boot_size() {
+ 	get_image "$@" | dd bs=4 count=1 skip=37 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
+ }
+ 
++tplink_pharos_check_image() {
++	local magic_long="$(get_magic_long "$1")"
++	[ "$magic_long" != "7f454c46" ] && {
++		echo "Invalid image magic '$magic_long'"
++		return 1
++	}
++
++	local model_string="$(tplink_pharos_get_model_string)"
++	local line
++
++	# Here $1 is given to dd directly instead of get_image as otherwise the skip
++	# will take almost a second (as dd can't seek then)
++	#
++	# This will fail if the image isn't local, but that's fine: as the
++	# read loop won't be executed at all, it will return true, so the image
++	# is accepted (loading the first 1.5M of a remote image for this check seems
++	# a bit extreme)
++	dd if="$1" bs=1 skip=1511432 count=1024 2>/dev/null | while read line; do
++		[ "$line" == "$model_string" ] && break
++	done || {
++		echo "Unsupported image (model not in support-list)"
++		return 1
++	}
++
++	return 0
++}
++
+ seama_get_type_magic() {
+ 	get_image "$@" | dd bs=1 count=4 skip=53 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'
+ }
+@@ -214,6 +241,11 @@ platform_check_image() {
+ 		return 0
+ 		;;
+ 
++	cpe510)
++		tplink_pharos_check_image "$1" && return 0
++		return 1
++		;;
++
+ 	dir-825-b1 | \
+ 	tew-673gru)
+ 		dir825b_check_image "$1" && return 0
+diff --git a/target/linux/ar71xx/config-3.10 b/target/linux/ar71xx/config-3.10
+index 9a8378a..2f05ec6 100644
+--- a/target/linux/ar71xx/config-3.10
++++ b/target/linux/ar71xx/config-3.10
+@@ -39,6 +39,7 @@ CONFIG_ATH79_MACH_AW_NR580=y
+ CONFIG_ATH79_MACH_BHU_BXU2000N2_A=y
+ CONFIG_ATH79_MACH_CAP4200AG=y
+ CONFIG_ATH79_MACH_CARAMBOLA2=y
++CONFIG_ATH79_MACH_CPE510=y
+ CONFIG_ATH79_MACH_DB120=y
+ CONFIG_ATH79_MACH_DIR_505_A1=y
+ CONFIG_ATH79_MACH_DIR_600_A1=y
+diff --git a/target/linux/ar71xx/files/arch/mips/ath79/mach-cpe510.c b/target/linux/ar71xx/files/arch/mips/ath79/mach-cpe510.c
+new file mode 100644
+index 0000000..8bf5c0f
+--- /dev/null
++++ b/target/linux/ar71xx/files/arch/mips/ath79/mach-cpe510.c
+@@ -0,0 +1,107 @@
++/*
++ *  TP-LINK CPE210/220/510/520 board support
++ *
++ *  Copyright (C) 2014 Matthias Schiffer <mschiffer@universe-factory.net>
++ *
++ *  This program is free software; you can redistribute it and/or modify it
++ *  under the terms of the GNU General Public License version 2 as published
++ *  by the Free Software Foundation.
++ */
++
++#include <linux/gpio.h>
++#include <linux/platform_device.h>
++
++#include <asm/mach-ath79/ath79.h>
++#include <asm/mach-ath79/ar71xx_regs.h>
++
++#include "common.h"
++#include "dev-eth.h"
++#include "dev-gpio-buttons.h"
++#include "dev-leds-gpio.h"
++#include "dev-m25p80.h"
++#include "dev-wmac.h"
++#include "machtypes.h"
++
++
++#define CPE510_GPIO_LED_LAN0	11
++#define CPE510_GPIO_LED_LAN1	12
++#define CPE510_GPIO_LED_L1	13
++#define CPE510_GPIO_LED_L2	14
++#define CPE510_GPIO_LED_L3	15
++#define CPE510_GPIO_LED_L4	16
++
++#define CPE510_GPIO_BTN_RESET	4
++
++#define CPE510_KEYS_POLL_INTERVAL	20 /* msecs */
++#define CPE510_KEYS_DEBOUNCE_INTERVAL	(3 * CPE510_KEYS_POLL_INTERVAL)
++
++
++static struct gpio_led cpe510_leds_gpio[] __initdata = {
++	{
++		.name		= "tp-link:green:lan0",
++		.gpio		= CPE510_GPIO_LED_LAN0,
++		.active_low	= 1,
++	}, {
++		.name		= "tp-link:green:lan1",
++		.gpio		= CPE510_GPIO_LED_LAN1,
++		.active_low	= 1,
++	}, {
++		.name		= "tp-link:green:link1",
++		.gpio		= CPE510_GPIO_LED_L1,
++		.active_low	= 1,
++	}, {
++		.name		= "tp-link:green:link2",
++		.gpio		= CPE510_GPIO_LED_L2,
++		.active_low	= 1,
++	}, {
++		.name		= "tp-link:green:link3",
++		.gpio		= CPE510_GPIO_LED_L3,
++		.active_low	= 1,
++	}, {
++		.name		= "tp-link:green:link4",
++		.gpio		= CPE510_GPIO_LED_L4,
++		.active_low	= 1,
++	},
++};
++
++static struct gpio_keys_button cpe510_gpio_keys[] __initdata = {
++	{
++		.desc		= "Reset button",
++		.type		= EV_KEY,
++		.code		= KEY_RESTART,
++		.debounce_interval = CPE510_KEYS_DEBOUNCE_INTERVAL,
++		.gpio		= CPE510_GPIO_BTN_RESET,
++		.active_low	= 1,
++	}
++};
++
++
++static void __init cpe510_setup(void)
++{
++	u8 *mac = (u8 *) KSEG1ADDR(0x1f830008);
++	u8 *ee = (u8 *) KSEG1ADDR(0x1fff1000);
++
++	/* Disable JTAG, enabling GPIOs 0-3 */
++	/* Configure OBS4 line, for GPIO 4*/
++	ath79_gpio_function_setup(AR934X_GPIO_FUNC_JTAG_DISABLE,
++				  AR934X_GPIO_FUNC_CLK_OBS4_EN);
++
++	ath79_register_leds_gpio(-1, ARRAY_SIZE(cpe510_leds_gpio),
++				 cpe510_leds_gpio);
++
++	ath79_register_gpio_keys_polled(1, CPE510_KEYS_POLL_INTERVAL,
++					ARRAY_SIZE(cpe510_gpio_keys),
++					cpe510_gpio_keys);
++
++	ath79_register_m25p80(NULL);
++
++	ath79_register_mdio(1, 0);
++	ath79_init_mac(ath79_eth1_data.mac_addr, mac, 0);
++	ath79_eth1_data.phy_if_mode = PHY_INTERFACE_MODE_GMII;
++	ath79_register_eth(1);
++
++	ath79_register_wmac(ee, mac);
++}
++
++MIPS_MACHINE(ATH79_MACH_CPE510, "CPE510", "TP-LINK CPE210/220/510/520",
++	     cpe510_setup);
+diff --git a/target/linux/ar71xx/generic/profiles/tp-link.mk b/target/linux/ar71xx/generic/profiles/tp-link.mk
+index 78333be..a9d170c 100644
+--- a/target/linux/ar71xx/generic/profiles/tp-link.mk
++++ b/target/linux/ar71xx/generic/profiles/tp-link.mk
+@@ -16,6 +16,17 @@ endef
+ $(eval $(call Profile,ARCHERC7))
+ 
+ 
++define Profile/CPE510
++	NAME:=TP-LINK CPE210/220/510/520
++	PACKAGES:=rssileds
++endef
++
++define Profile/CPE510/Description
++	Package set optimized for the TP-LINK CPE210/220/510/520.
++endef
++$(eval $(call Profile,CPE510))
++
++
+ define Profile/TLMR10U
+ 	NAME:=TP-LINK TL-MR10U
+ 	PACKAGES:=kmod-usb-core kmod-usb2
+diff --git a/target/linux/ar71xx/image/Makefile b/target/linux/ar71xx/image/Makefile
+index 683c238..5ff7f35 100644
+--- a/target/linux/ar71xx/image/Makefile
++++ b/target/linux/ar71xx/image/Makefile
+@@ -261,6 +261,7 @@ cameo_ap121_mtdlayout_8M=mtdparts=spi0.0:64k(u-boot)ro,64k(art)ro,64k(mac)ro,64k
+ cameo_db120_mtdlayout=mtdparts=spi0.0:64k(uboot)ro,64k(nvram)ro,15936k(firmware),192k(lang)ro,64k(mac)ro,64k(art)ro
+ cameo_db120_mtdlayout_8M=mtdparts=spi0.0:64k(uboot)ro,64k(nvram)ro,7872k(firmware),128k(lang)ro,64k(art)ro
+ cap4200ag_mtdlayout=mtdparts=spi0.0:256k(u-boot),64k(u-boot-env),320k(custom)ro,1536k(kernel),12096k(rootfs),2048k(failsafe),64k(art),13632k@0xa0000(firmware)
++cpe510_mtdlayout=mtdparts=spi0.0:128k(u-boot)ro,64k(pation-table)ro,64k(product-info)ro,1536k(kernel),6144k(rootfs),192k(config)ro,64k(ART)ro,7680k@0x40000(firmware)
+ eap300v2_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env),320k(custom),13632k(firmware),2048k(failsafe),64k(art)ro
+ db120_mtdlayout=mtdparts=spi0.0:256k(u-boot)ro,64k(u-boot-env)ro,6336k(rootfs),1408k(kernel),64k(nvram),64k(art)ro,7744k@0x50000(firmware)
+ cameo_ap94_mtdlayout=mtdparts=spi0.0:256k(uboot)ro,64k(config)ro,6208k(firmware),64k(caldata)ro,1600k(unknown)ro,64k@0x7f0000(caldata_copy)
+@@ -811,6 +812,32 @@ define Image/Build/TPLINK-LZMA/initramfs
+ endef
+ 
+ 
++Image/Build/TPLINK-SAFELOADER/buildkernel=$(call PatchKernelLzma,$(2),$(3) $(4))
++
++define Image/Build/TPLINK-SAFELOADER
++	-rm -rf $(KDIR)/lzma-loader
++	$(LOADER_MAKE) LOADER=loader-$(2).elf\
++		LZMA_TEXT_START=0x80a00000 LOADADDR=0x80060000 \
++		LOADER_DATA="$(KDIR_TMP)/vmlinux-$(2).bin.lzma" BOARD="$(2)" \
++		compile loader.elf
++
++	-$(STAGING_DIR_HOST)/bin/tplink-safeloader \
++		-B $(5) \
++		-k $(KDIR)/loader-$(2).elf \
++		-r $(KDIR)/root.$(1) \
++		-V $(REVISION) \
++		-j \
++		-o $(call factoryname,$(1),$(2))
++	-$(STAGING_DIR_HOST)/bin/tplink-safeloader \
++		-B $(5) \
++		-k $(KDIR)/loader-$(2).elf \
++		-r $(KDIR)/root.$(1) \
++		-V $(REVISION) \
++		-j -S \
++		-o $(call sysupname,$(1),$(2))
++endef
++
++
+ define Image/Build/CyberTAN
+ 	echo -n '' > $(KDIR_TMP)/empty.bin
+ 	$(STAGING_DIR_HOST)/bin/trx -o $(KDIR)/image.tmp \
+@@ -1226,6 +1253,8 @@ $(eval $(call SingleProfile,TPLINK-LZMA,64kraw,TLWDR4310V1,tl-wdr4310-v1,TL-WDR4
+ $(eval $(call SingleProfile,TPLINK-LZMA,64kraw,TLWDR4900V2,tl-wdr4900-v2,TL-WDR4900-v2,ttyS0,115200,0x49000002,1,8Mlzma))
+ $(eval $(call SingleProfile,TPLINK-LZMA,64kraw,MW4530RV1,mw4530r-v1,TL-WDR4300,ttyS0,115200,0x45300001,1,8Mlzma))
+ 
++$(eval $(call SingleProfile,TPLINK-SAFELOADER,64kraw,CPE510,cpe210-220-510-520,CPE510,ttyS0,115200,$$(cpe510_mtdlayout),CPE510))
++
+ $(eval $(call SingleProfile,TPLINK-LZMA,64kraw,SMART-300,smart-300,SMART-300,ttyS0,115200,0x93410001,1,8Mlzma))
+ 
+ $(eval $(call SingleProfile,TPLINK-LZMA,64kraw,OOLITE,oolite,GS-OOLITE,ttyATH0,115200,0x3C000101,1,16Mlzma))
+diff --git a/target/linux/ar71xx/patches-3.10/610-MIPS-ath79-openwrt-machines.patch b/target/linux/ar71xx/patches-3.10/610-MIPS-ath79-openwrt-machines.patch
+index 3669f26..516d52d 100644
+--- a/target/linux/ar71xx/patches-3.10/610-MIPS-ath79-openwrt-machines.patch
++++ b/target/linux/ar71xx/patches-3.10/610-MIPS-ath79-openwrt-machines.patch
+@@ -1,6 +1,6 @@
+ --- a/arch/mips/ath79/machtypes.h
+ +++ b/arch/mips/ath79/machtypes.h
+-@@ -16,22 +16,144 @@
++@@ -16,22 +16,145 @@
+  
+  enum ath79_mach_type {
+  	ATH79_MACH_GENERIC = 0,
+@@ -24,6 +24,7 @@
+ +	ATH79_MACH_BHU_BXU2000N2_A1,	/* BHU BXU2000n-2 A1 */
+ +	ATH79_MACH_CAP4200AG,		/* Senao CAP4200AG */
+ +	ATH79_MACH_CARAMBOLA2,		/* 8devices Carambola2 */
+++	ATH79_MACH_CPE510,		/* TP-LINK CPE510 */
+  	ATH79_MACH_DB120,		/* Atheros DB120 reference board */
+  	ATH79_MACH_PB44,		/* Atheros PB44 reference board */
+ +	ATH79_MACH_DIR_505_A1,		/* D-Link DIR-505 rev. A1 */
+@@ -209,7 +210,7 @@
+  config ATH79_MACH_AP121
+  	bool "Atheros AP121 reference board"
+  	select SOC_AR933X
+-@@ -9,64 +64,736 @@ config ATH79_MACH_AP121
++@@ -9,64 +64,745 @@ config ATH79_MACH_AP121
+  	select ATH79_DEV_GPIO_BUTTONS
+  	select ATH79_DEV_LEDS_GPIO
+  	select ATH79_DEV_M25P80
+@@ -709,6 +710,15 @@
+  
+ -config ATH79_MACH_AP81
+ -	bool "Atheros AP81 reference board"
+++config ATH79_MACH_CPE510
+++       bool "TP-LINK CPE510 support"
+++       select SOC_AR934X
+++       select ATH79_DEV_ETH
+++       select ATH79_DEV_GPIO_BUTTONS
+++       select ATH79_DEV_LEDS_GPIO
+++       select ATH79_DEV_M25P80
+++       select ATH79_DEV_WMAC
+++
+ +config ATH79_MACH_TL_MR11U
+ +	bool "TP-LINK TL-MR11U/TL-MR3040 support"
+ +	select SOC_AR933X
+@@ -972,7 +982,7 @@
+  
+  config ATH79_MACH_UBNT_XM
+  	bool "Ubiquiti Networks XM/UniFi boards"
+-@@ -83,6 +810,65 @@ config ATH79_MACH_UBNT_XM
++@@ -83,6 +819,65 @@ config ATH79_MACH_UBNT_XM
+  	  Say 'Y' here if you want your kernel to support the
+  	  Ubiquiti Networks XM (rev 1.0) board.
+  
+@@ -1038,7 +1048,7 @@
+  endmenu
+  
+  config SOC_AR71XX
+-@@ -132,7 +918,10 @@ config ATH79_DEV_DSA
++@@ -132,7 +927,10 @@ config ATH79_DEV_DSA
+  config ATH79_DEV_ETH
+  	def_bool n
+  
+@@ -1050,7 +1060,7 @@
+  	def_bool n
+  
+  config ATH79_DEV_GPIO_BUTTONS
+-@@ -164,4 +953,7 @@ config ATH79_PCI_ATH9K_FIXUP
++@@ -164,4 +962,7 @@ config ATH79_PCI_ATH9K_FIXUP
+  config ATH79_ROUTERBOOT
+  	def_bool n
+  
+@@ -1060,7 +1070,7 @@
+  endif
+ --- a/arch/mips/ath79/Makefile
+ +++ b/arch/mips/ath79/Makefile
+-@@ -38,9 +38,90 @@ obj-$(CONFIG_ATH79_ROUTERBOOT)		+= route
++@@ -38,9 +38,91 @@ obj-$(CONFIG_ATH79_ROUTERBOOT)		+= route
+  #
+  # Machines
+  #
+@@ -1079,6 +1089,7 @@
+ +obj-$(CONFIG_ATH79_MACH_AW_NR580)	+= mach-aw-nr580.o
+ +obj-$(CONFIG_ATH79_MACH_BHU_BXU2000N2_A)+= mach-bhu-bxu2000n2-a.o
+ +obj-$(CONFIG_ATH79_MACH_CAP4200AG)	+= mach-cap4200ag.o
+++obj-$(CONFIG_ATH79_MACH_CPE510)		+= mach-cpe510.o
+  obj-$(CONFIG_ATH79_MACH_DB120)		+= mach-db120.o
+ +obj-$(CONFIG_ATH79_MACH_DIR_505_A1)	+= mach-dir-505-a1.o
+ +obj-$(CONFIG_ATH79_MACH_DIR_600_A1)	+= mach-dir-600-a1.o

+ 1 - 0
targets/ar71xx-generic/kernel-config

@@ -68,6 +68,7 @@ CONFIG_ATH79_MACH_AP136=y
 CONFIG_ATH79_MACH_AP81=y
 CONFIG_ATH79_MACH_AP83=y
 CONFIG_ATH79_MACH_AP96=y
+CONFIG_ATH79_MACH_CPE510=y
 CONFIG_ATH79_MACH_DB120=y
 CONFIG_ATH79_MACH_PB42=y
 CONFIG_ATH79_MACH_PB44=y

+ 7 - 0
targets/ar71xx-generic/profiles.mk

@@ -2,6 +2,13 @@
 
 ## TP-Link
 
+# CPE210/220/510/520
+$(eval $(call GluonProfile,CPE510,rssileds))
+$(eval $(call GluonModel,CPE510,cpe210-220-510-520-squashfs,tp-link-cpe210-v1.0))
+$(eval $(call GluonModel,CPE510,cpe210-220-510-520-squashfs,tp-link-cpe220-v1.0))
+$(eval $(call GluonModel,CPE510,cpe210-220-510-520-squashfs,tp-link-cpe510-v1.0))
+$(eval $(call GluonModel,CPE510,cpe210-220-510-520-squashfs,tp-link-cpe520-v1.0))
+
 # TL-WR703N v1
 $(eval $(call GluonProfile,TLWR703))
 $(eval $(call GluonModel,TLWR703,tl-wr703n-v1-squashfs,tp-link-tl-wr703n-v1))