2 * Program to link against Agere FW images, and dump contents to
3 * binary files for loading directly by linux drivers.
5 * Copyright (C) 2008 David Kilroy
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 /* Output format (LE numbers)
24 * vers [6] The text HFW and 3 ASCII digits indicating version
25 * headersize [2] Size of header inc vers, headersize and length
26 * entry point [4] NIC address of entry point
27 * blocks [4] Number of blocks (n)
28 * blk_offset [4] Offset to block data
29 * pdr_offset [4] Offset to PDR data
30 * pri_offset [4] Offset to primary plug data
31 * cpt_offset [4] Offset to compatibility data
32 * signature [arb] firmware signature
51 * addr [4] NIC address to program data
52 * length [2] Number of bytes of data to program
53 * data [arbitrary] Data to program
56 * 0xFFFFFFFF [4] BLOCK_END identifier
57 * 0x0000 [2] zero length
59 * pda_n and pri_n are:
60 * id [4] (Primary) PDA identifier
61 * addr [4] Address to program (Primary) PDA
62 * len [4] Number of bytes to program
64 * pda_term and pri_n are:
65 * 0x00000000 [4] PDI_END
70 * size [2] Length of LTV - ((n_bytes/2) - 1)
71 * code [2] LTV code - 0xFD21 (FW compatibility range)
72 * 0xFD22 (Modem I/F compatibility range)
73 * 0xFD23 (Controller I/F compatibility range)
74 * role [2] Who this restriction applies to?
78 * spec_1 Specifications
87 * There is more information available in the driver. In particular
88 * whether the block is supposed to be programmed to NV or volatile,
91 * Apart from the header, the output format is compatible with the
92 * spectrum_cs image. The header is arbitrary.
102 #define AP_SUFFIX "_ap_fw.bin"
103 #define STA_SUFFIX "_sta_fw.bin"
104 #define VERSION "HFW000"
106 /* 0xAABB to 0xBBAA */
107 #define swap_bytes_16(value) \
108 ((((value) >> 8) & 0xFF) | \
109 (((value) & 0xFF) << 8))
110 /* 0xAABBCCDD to 0xDDCCBBAA */
111 #define reverse_bytes_32(value) \
112 ((((value) >> 24) & 0x0000FF) | \
113 (((value) >> 8) & 0x00FF00) | \
114 (((value) << 8) & 0xFF0000) | \
115 (((value) & 0xFF) << 24))
116 /* 0xAABBCCDD to 0xBBAADDCC */
117 #define swap_bytes_32(value) \
118 ((((value) >> 8) & 0x00FF00FF) | \
119 (((value) << 8) & 0xFF00FF00))
120 /* 0xAABBCCDD to 0xCCDDAABB */
121 #define swap_words_32(value) \
122 ((((value) >> 16) & 0x0000FFFF) | \
123 (((value) << 16) & 0xFFFF0000))
125 /* address 0 1 2 3 */
126 /* Pure LE stores 0x12345678 as 0x78 0x56 0x34 0x12 */
127 /* Pure BE stores 0x12345678 as 0x12 0x34 0x56 0x78 */
128 /* BEW+LEB stores 0x12345678 as 0x34 0x12 0x78 0x56 */
129 /* LEW+BEB stores 0x12345678 as 0x56 0x78 0x12 0x34 */
130 int host_bytes_in_word_be
= 0;
131 int host_words_in_dword_be
= 0;
133 #define host_to_le16(value) \
134 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
135 #define host_to_le32(value) \
136 (host_words_in_dword_be ? \
137 (host_bytes_in_word_be ? reverse_bytes_32(value) \
138 : swap_bytes_32(value)) : \
139 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
141 #define le16_to_host(value) \
142 (host_bytes_in_word_be ? swap_bytes_16(value) : (value))
143 #define le32_to_host(value) \
144 (host_words_in_dword_be ? \
145 (host_bytes_in_word_be ? reverse_bytes_32(value) \
146 : swap_bytes_32(value)) : \
147 (host_bytes_in_word_be ? swap_words_32(value) : (value)))
149 /* Use C99 exact width types */
150 typedef uint32_t u32
;
151 typedef uint16_t u16
;
154 /* Checking endianess at runtime because performance isn't an issue,
155 * and I'd rather not add a configure step since this is slotting into the
156 * Agere source code. */
157 void check_endianess(void) {
164 data
.dword
= 0x12345678;
165 if (data
.word
[0] == 0x1234) {
166 host_words_in_dword_be
= 1;
168 else if (data
.word
[0] == 0x5678) {
169 host_words_in_dword_be
= 0;
171 fprintf(stderr
, "Can't determine endianess of host!\n");
175 data
.word
[0] = 0x1234;
176 if (data
.byte
[0] == 0x12) {
177 host_bytes_in_word_be
= 1;
178 } else if (data
.byte
[0] == 0x34) {
179 host_bytes_in_word_be
= 0;
181 fprintf(stderr
, "Can't determine endianess of host!\n");
184 if (host_bytes_in_word_be
== host_words_in_dword_be
) {
185 fprintf (stdout
, "Detected %s host\n",
186 host_bytes_in_word_be
? "big endian" : "little endian");
188 fprintf (stdout
, "Detected host with mixed endianess\n");
193 size_t count_blocks(memimage
*image
)
196 # define MEMBLOCK memblock
197 # define BLKSIZE p->size
198 # define IF_HAVE_SEGMENT
200 # define MEMBLOCK CFG_PROG_STRCT
201 # define BLKSIZE p->len
202 # define IF_HAVE_SEGMENT if (p->segment_size)
205 MEMBLOCK
*p
= image
->codep
;
209 /* Ignore zero data segments which will not be written */
220 size_t count_pdr(plugrecord
*r
)
234 size_t acc_block_size(memimage
*image
)
237 # define MEMBLOCK memblock
238 # define BLKSIZE p->size
239 # define SEGSIZE p->size
240 # define IF_HAVE_SEGMENT
242 # define MEMBLOCK CFG_PROG_STRCT
243 # define BLKSIZE p->len
244 # define SEGSIZE p->segment_size
245 # define IF_HAVE_SEGMENT if (p->segment_size)
248 MEMBLOCK
*p
= image
->codep
;
252 /* Ignore zero data segments which will not be written */
262 void dump_blocks(FILE* f
, memimage
*image
)
265 # define MEMBLOCK memblock
266 # define BLKSIZE p->size
267 # define SEGSIZE p->size
268 # define NICADDR p->addr
269 # define SEGDATA &(p->data[4]) /* Avoid initial CRC */
270 # define IF_HAVE_SEGMENT
272 # define MEMBLOCK CFG_PROG_STRCT
273 # define BLKSIZE p->len
274 # define SEGSIZE p->segment_size
275 # define NICADDR p->nic_addr
276 # define SEGDATA p->host_addr
277 # define IF_HAVE_SEGMENT if (p->segment_size)
280 MEMBLOCK
*p
= image
->codep
;
281 u8 block_hdr
[sizeof(NICADDR
) + sizeof(SEGSIZE
)];
282 u32
*addr
= (u32
*) &block_hdr
[0];
283 u16
*size
= (u16
*) &block_hdr
[sizeof(NICADDR
)];
289 /* There is data to program in this block */
290 *addr
= host_to_le32(NICADDR
);
291 *size
= host_to_le16(SEGSIZE
);
292 fwrite (&block_hdr
, 1, sizeof(block_hdr
), f
);
293 fwrite (SEGDATA
, 1, SEGSIZE
, f
);
297 *addr
= host_to_le32(0xFFFFFFFFu
); /* Agree with spectrum BLOCK_END */
298 *size
= host_to_le16(0u);
300 fwrite (&block_hdr
, 1, sizeof(block_hdr
), f
);
305 void dump_pdr(FILE *f
, plugrecord
*r
)
307 u8 pdr
[sizeof(r
->code
) + sizeof(r
->addr
) + sizeof(r
->len
)];
308 u32
*code
= (u32
*) &pdr
[0];
309 u32
*addr
= (u32
*) &pdr
[sizeof(r
->code
)];
310 u32
*len
= (u32
*) &pdr
[sizeof(r
->code
) + sizeof(r
->addr
)];
317 *code
= host_to_le32(r
->code
);
318 *addr
= host_to_le32(r
->addr
);
319 *len
= host_to_le32(r
->len
);
320 fwrite(&pdr
, 1, sizeof(pdr
), f
);
324 /* Terminate the PDR list */
328 fwrite(&pdr
, 1, sizeof(pdr
), f
);
332 void dump_compat(FILE *f
, CFG_RANGE20_STRCT
*c
)
335 #define VARIANT_STRUCT variant
336 #define VARIANT_NO number
338 #define VARIANT_STRUCT var_rec
339 #define VARIANT_NO variant
341 u8 hdr
[sizeof(c
->id
) + sizeof(c
->typ
) + sizeof(c
->role
) + sizeof(c
->id
)];
342 u8 spec
[sizeof(c
->VARIANT_STRUCT
[0])];
343 u16
*len
= (u16
*) &hdr
[0];
344 u16
*typ
= (u16
*) &hdr
[sizeof(c
->len
)];
345 u16
*role
= (u16
*) &hdr
[sizeof(c
->len
) + sizeof(c
->typ
)];
346 u16
*id
= (u16
*) &hdr
[sizeof(c
->len
) + sizeof(c
->typ
) + sizeof(c
->role
)];
347 u16
*variant
= (u16
*) &spec
[0];
348 u16
*bottom
= (u16
*) &spec
[sizeof(c
->VARIANT_STRUCT
[0].VARIANT_NO
)];
349 u16
*top
= (u16
*) &spec
[sizeof(c
->VARIANT_STRUCT
[0].VARIANT_NO
) + sizeof(c
->VARIANT_STRUCT
[0].bottom
)];
354 *len
= host_to_le16(c
->len
);
355 *typ
= host_to_le16(c
->typ
);
356 *role
= host_to_le16(c
->role
);
357 *id
= host_to_le16(c
->id
);
358 fwrite(&hdr
, 1, sizeof(hdr
), f
);
360 for (i
= 0; i
< sizeof(c
->VARIANT_STRUCT
)/sizeof(c
->VARIANT_STRUCT
[0]); i
++)
362 *variant
= host_to_le16(c
->VARIANT_STRUCT
[i
].VARIANT_NO
);
363 *bottom
= host_to_le16(c
->VARIANT_STRUCT
[i
].bottom
);
364 *top
= host_to_le16(c
->VARIANT_STRUCT
[i
].top
);
365 fwrite(&spec
, 1, sizeof(spec
), f
);
372 memset(&hdr
[0], 0, sizeof(hdr
));
373 memset(&spec
[0], 0, sizeof(spec
));
374 fwrite(&hdr
, 1, sizeof(hdr
), f
);
375 for (i
= 0; i
< sizeof(c
->VARIANT_STRUCT
)/sizeof(c
->VARIANT_STRUCT
[0]); i
++)
377 fwrite(&spec
, 1, sizeof(spec
), f
);
383 void dump_image(FILE* f
, memimage
*image
)
389 #define PDA place_holder_1
390 #define PRI place_holder_2
393 u32 blocks
= count_blocks(image
);
394 u32 blk_offset
= 0; /* Immediately after header */
395 u32 pdr_offset
= (acc_block_size(image
) +
396 ((blocks
+ 1) * (sizeof(u32
) + sizeof(u16
))));
397 u32 pri_offset
= pdr_offset
+
398 ((count_pdr(image
->PDA
) + 1) * sizeof(u32
) * 3);
399 u32 cpt_offset
= pri_offset
+
400 ((count_pdr(image
->PRI
) + 1) * sizeof(u32
) * 3);
401 u16 headersize
= ((sizeof(VERSION
)-1) +
405 + sizeof(image
->signature
)
408 u32
*ptr
= &image_header
[0];
409 fwrite (VERSION
, 1, sizeof(VERSION
)-1, f
);
410 headersize
= host_to_le16(headersize
);
411 fwrite (&headersize
, 1, sizeof(headersize
), f
);
413 *ptr
= host_to_le32(image
->execution
);
415 *ptr
= host_to_le32(blocks
);
417 *ptr
= host_to_le32(blk_offset
);
419 *ptr
= host_to_le32(pdr_offset
);
421 *ptr
= host_to_le32(pri_offset
);
423 *ptr
= host_to_le32(cpt_offset
);
425 fwrite (&image_header
, 1, sizeof(image_header
), f
);
427 fwrite (&image
->signature
, 1, sizeof(image
->signature
), f
);
430 dump_blocks(f
, image
);
431 dump_pdr(f
, image
->PDA
);
432 dump_pdr(f
, image
->PRI
);
433 dump_compat(f
, image
->compat
);
440 extern memimage station
;
442 extern memimage fw_image
;
444 #define station fw_image
447 int main (int argc
, char** argv
)
458 printf("Please specify a root filename.\n"
459 "%s will be appended for primary firmware\n"
460 "%s will be appended for secondary firmaware\n",
461 AP_SUFFIX
, STA_SUFFIX
);
467 len
= strlen(argv
[1]);
469 if (ap
.identity
->comp_id
!= COMP_ID_FW_AP
)
472 ap_filename
= malloc(len
+ sizeof(AP_SUFFIX
) + 1);
475 fprintf(stderr
, "Out of memory\n");
478 strncpy(ap_filename
, argv
[1], len
);
479 ap_filename
[len
] = 0;
480 strcat(ap_filename
, AP_SUFFIX
);
482 ap_file
= fopen(ap_filename
, "w");
485 fprintf(stderr
, "Can't open %s for writing\n", ap_filename
);
490 dump_image(ap_file
, &ap
);
492 fprintf (stdout
, "Written %s\n", ap_filename
);
497 if (station
.identity
->comp_id
!= COMP_ID_FW_STA
)
500 sta_filename
= malloc(len
+ sizeof(STA_SUFFIX
) + 1);
503 fprintf(stderr
, "Out of memory\n");
506 strncpy(sta_filename
, argv
[1], len
);
507 sta_filename
[len
] = 0;
508 strcat(sta_filename
,STA_SUFFIX
);
510 sta_file
= fopen(sta_filename
,"w");
513 fprintf(stderr
, "Can't open %s for writing\n", sta_filename
);
518 dump_image(sta_file
, &station
);
520 fprintf (stdout
, "Written %s\n", sta_filename
);