2 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation.
10 #include <linux/kernel.h>
11 #include <linux/slab.h>
12 #include <linux/vmalloc.h>
13 #include <linux/magic.h>
15 #include <linux/mtd/mtd.h>
16 #include <linux/mtd/partitions.h>
18 #define TPLINK_NUM_PARTS 5
19 #define TPLINK_HEADER_V1 0x01000000
22 #define TPLINK_ART_LEN 0x10000
23 #define TPLINK_KERNEL_OFFS 0x20000
25 struct tplink_fw_header
{
26 uint32_t version
; /* header version */
29 uint32_t hw_id
; /* hardware id */
30 uint32_t hw_rev
; /* hardware revision */
32 uint8_t md5sum1
[MD5SUM_LEN
];
34 uint8_t md5sum2
[MD5SUM_LEN
];
36 uint32_t kernel_la
; /* kernel load address */
37 uint32_t kernel_ep
; /* kernel entry point */
38 uint32_t fw_length
; /* total length of the firmware */
39 uint32_t kernel_ofs
; /* kernel data offset */
40 uint32_t kernel_len
; /* kernel data length */
41 uint32_t rootfs_ofs
; /* rootfs data offset */
42 uint32_t rootfs_len
; /* rootfs data length */
43 uint32_t boot_ofs
; /* bootloader data offset */
44 uint32_t boot_len
; /* bootloader data length */
46 } __attribute__ ((packed
));
48 static struct tplink_fw_header
*
49 tplink_read_header(struct mtd_info
*mtd
, size_t offset
)
51 struct tplink_fw_header
*header
;
57 header
= vmalloc(sizeof(*header
));
61 header_len
= sizeof(struct tplink_fw_header
);
62 ret
= mtd
->read(mtd
, offset
, header_len
, &retlen
,
63 (unsigned char *) header
);
67 if (retlen
!= header_len
)
71 t
= be32_to_cpu(header
->version
);
72 if (t
!= TPLINK_HEADER_V1
)
75 t
= be32_to_cpu(header
->kernel_ofs
);
87 static int tplink_check_squashfs_magic(struct mtd_info
*mtd
, size_t offset
)
93 ret
= mtd
->read(mtd
, offset
, sizeof(magic
), &retlen
,
94 (unsigned char *) &magic
);
98 if (retlen
!= sizeof(magic
))
101 if (le32_to_cpu(magic
) != SQUASHFS_MAGIC
)
107 static int tplink_parse_partitions(struct mtd_info
*master
,
108 struct mtd_partition
**pparts
,
109 unsigned long origin
)
111 struct mtd_partition
*parts
;
112 struct tplink_fw_header
*header
;
116 size_t rootfs_offset
;
117 size_t squashfs_offset
;
120 nr_parts
= TPLINK_NUM_PARTS
;
121 parts
= kzalloc(nr_parts
* sizeof(struct mtd_partition
), GFP_KERNEL
);
127 offset
= TPLINK_KERNEL_OFFS
;
129 header
= tplink_read_header(master
, offset
);
131 pr_notice("%s: no TP-Link header found\n", master
->name
);
136 squashfs_offset
= offset
+ sizeof(struct tplink_fw_header
) +
137 be32_to_cpu(header
->kernel_len
);
139 ret
= tplink_check_squashfs_magic(master
, squashfs_offset
);
141 rootfs_offset
= squashfs_offset
;
143 rootfs_offset
= offset
+ be32_to_cpu(header
->rootfs_ofs
);
145 art_offset
= master
->size
- TPLINK_ART_LEN
;
147 parts
[0].name
= "u-boot";
149 parts
[0].size
= offset
;
150 parts
[0].mask_flags
= MTD_WRITEABLE
;
152 parts
[1].name
= "kernel";
153 parts
[1].offset
= offset
;
154 parts
[1].size
= rootfs_offset
- offset
;
156 parts
[2].name
= "rootfs";
157 parts
[2].offset
= rootfs_offset
;
158 parts
[2].size
= art_offset
- rootfs_offset
;
160 parts
[3].name
= "art";
161 parts
[3].offset
= art_offset
;
162 parts
[3].size
= TPLINK_ART_LEN
;
163 parts
[3].mask_flags
= MTD_WRITEABLE
;
165 parts
[4].name
= "firmware";
166 parts
[4].offset
= offset
;
167 parts
[4].size
= art_offset
- offset
;
181 static struct mtd_part_parser tplink_parser
= {
182 .owner
= THIS_MODULE
,
183 .parse_fn
= tplink_parse_partitions
,
187 static int __init
tplink_parser_init(void)
189 return register_mtd_parser(&tplink_parser
);
192 module_init(tplink_parser_init
);
194 MODULE_LICENSE("GPL v2");
195 MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");