1 | /* $NetBSD: fwcrom.c,v 1.17 2014/11/21 23:37:25 joerg Exp $ */ |
2 | /*- |
3 | * Copyright (c) 2002-2003 |
4 | * Hidetoshi Shimokawa. All rights reserved. |
5 | * |
6 | * Redistribution and use in source and binary forms, with or without |
7 | * modification, are permitted provided that the following conditions |
8 | * are met: |
9 | * 1. Redistributions of source code must retain the above copyright |
10 | * notice, this list of conditions and the following disclaimer. |
11 | * 2. Redistributions in binary form must reproduce the above copyright |
12 | * notice, this list of conditions and the following disclaimer in the |
13 | * documentation and/or other materials provided with the distribution. |
14 | * 3. All advertising materials mentioning features or use of this software |
15 | * must display the following acknowledgement: |
16 | * |
17 | * This product includes software developed by Hidetoshi Shimokawa. |
18 | * |
19 | * 4. Neither the name of the author nor the names of its contributors |
20 | * may be used to endorse or promote products derived from this software |
21 | * without specific prior written permission. |
22 | * |
23 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
24 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
25 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
26 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
27 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
28 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
29 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
30 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
31 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
32 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
33 | * SUCH DAMAGE. |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: fwcrom.c,v 1.17 2014/11/21 23:37:25 joerg Exp $" ); |
38 | |
39 | #include <sys/param.h> |
40 | #ifdef _KERNEL |
41 | #include <sys/device.h> |
42 | #include <sys/errno.h> |
43 | #include <sys/systm.h> |
44 | #else |
45 | #include <stdio.h> |
46 | #include <string.h> |
47 | #endif |
48 | #include <dev/ieee1394/firewire.h> |
49 | #include <dev/ieee1394/iec13213.h> |
50 | |
51 | #define MAX_ROM (1024 - sizeof(uint32_t) * 5) |
52 | #define CROM_END(cc) ((char *)(cc)->stack[0].dir + MAX_ROM - 1) |
53 | |
54 | void |
55 | crom_init_context(struct crom_context *cc, uint32_t *p) |
56 | { |
57 | struct csrhdr *hdr; |
58 | |
59 | hdr = (struct csrhdr *)p; |
60 | if (hdr->info_len <= 1) { |
61 | /* minimum or invalid ROM */ |
62 | cc->depth = -1; |
63 | return; |
64 | } |
65 | p += 1 + hdr->info_len; |
66 | |
67 | /* check size of root directory */ |
68 | if (((struct csrdirectory *)p)->crc_len == 0) { |
69 | cc->depth = -1; |
70 | return; |
71 | } |
72 | cc->depth = 0; |
73 | cc->stack[0].dir = (struct csrdirectory *)p; |
74 | cc->stack[0].index = 0; |
75 | } |
76 | |
77 | struct csrreg * |
78 | crom_get(struct crom_context *cc) |
79 | { |
80 | struct crom_ptr *ptr; |
81 | |
82 | ptr = &cc->stack[cc->depth]; |
83 | return &ptr->dir->entry[ptr->index]; |
84 | } |
85 | |
86 | void |
87 | crom_next(struct crom_context *cc) |
88 | { |
89 | struct crom_ptr *ptr; |
90 | struct csrreg *reg; |
91 | |
92 | if (cc->depth < 0) |
93 | return; |
94 | reg = crom_get(cc); |
95 | if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) { |
96 | if (cc->depth >= CROM_MAX_DEPTH) { |
97 | printf("crom_next: too deep\n" ); |
98 | goto again; |
99 | } |
100 | cc->depth++; |
101 | |
102 | ptr = &cc->stack[cc->depth]; |
103 | ptr->dir = (struct csrdirectory *)(reg + reg->val); |
104 | ptr->index = 0; |
105 | goto check; |
106 | } |
107 | again: |
108 | ptr = &cc->stack[cc->depth]; |
109 | ptr->index++; |
110 | check: |
111 | if (ptr->index < ptr->dir->crc_len && |
112 | (char *)crom_get(cc) <= CROM_END(cc)) |
113 | return; |
114 | |
115 | if (ptr->index < ptr->dir->crc_len) |
116 | printf("crom_next: bound check failed\n" ); |
117 | |
118 | if (cc->depth > 0) { |
119 | cc->depth--; |
120 | goto again; |
121 | } |
122 | /* no more data */ |
123 | cc->depth = -1; |
124 | } |
125 | |
126 | |
127 | struct csrreg * |
128 | crom_search_key(struct crom_context *cc, uint8_t key) |
129 | { |
130 | struct csrreg *reg; |
131 | |
132 | while (cc->depth >= 0) { |
133 | reg = crom_get(cc); |
134 | if (reg->key == key) |
135 | return reg; |
136 | crom_next(cc); |
137 | } |
138 | return NULL; |
139 | } |
140 | |
141 | int |
142 | crom_has_specver(uint32_t *p, uint32_t spec, uint32_t ver) |
143 | { |
144 | struct csrreg *reg; |
145 | struct crom_context c, *cc; |
146 | int state = 0; |
147 | |
148 | cc = &c; |
149 | crom_init_context(cc, p); |
150 | while (cc->depth >= 0) { |
151 | reg = crom_get(cc); |
152 | if (state == 0) { |
153 | if (reg->key == CSRKEY_SPEC && reg->val == spec) |
154 | state = 1; |
155 | else |
156 | state = 0; |
157 | } else { |
158 | if (reg->key == CSRKEY_VER && reg->val == ver) |
159 | return 1; |
160 | else |
161 | state = 0; |
162 | } |
163 | crom_next(cc); |
164 | } |
165 | return 0; |
166 | } |
167 | |
168 | void |
169 | crom_parse_text(struct crom_context *cc, char *buf, int len) |
170 | { |
171 | struct csrreg *reg; |
172 | struct csrtext *textleaf; |
173 | uint32_t *bp; |
174 | int i, qlen; |
175 | static const char nullstr[] = "(null)" ; |
176 | |
177 | if (cc->depth < 0) |
178 | return; |
179 | |
180 | reg = crom_get(cc); |
181 | switch (reg->key) { |
182 | case CROM_TEXTLEAF: |
183 | case CROM_TEXTLEAF2: |
184 | break; |
185 | default: |
186 | if ((char *)(reg + reg->val) > CROM_END(cc)) { |
187 | strncpy(buf, nullstr, len); |
188 | return; |
189 | } |
190 | break; |
191 | } |
192 | textleaf = (struct csrtext *)(reg + reg->val); |
193 | |
194 | if ((char *)textleaf + textleaf->crc_len > CROM_END(cc)) { |
195 | strncpy(buf, nullstr, len); |
196 | return; |
197 | } |
198 | |
199 | /* XXX should check spec and type */ |
200 | |
201 | bp = (uint32_t *)buf; |
202 | qlen = textleaf->crc_len - 2; |
203 | if (len < qlen * 4) |
204 | qlen = len/4; |
205 | for (i = 0; i < qlen; i++) |
206 | *bp++ = ntohl(textleaf->text[i]); |
207 | /* make sure to terminate the string */ |
208 | if (len <= qlen * 4) |
209 | buf[len - 1] = 0; |
210 | else |
211 | buf[qlen * 4] = 0; |
212 | } |
213 | |
214 | uint16_t |
215 | crom_crc(uint32_t *ptr, int len) |
216 | { |
217 | int i, shift; |
218 | uint32_t data, sum, crc = 0; |
219 | |
220 | for (i = 0; i < len; i++) { |
221 | data = ptr[i]; |
222 | for (shift = 28; shift >= 0; shift -= 4) { |
223 | sum = ((crc >> 12) ^ (data >> shift)) & 0xf; |
224 | crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum; |
225 | } |
226 | crc &= 0xffff; |
227 | } |
228 | return (uint16_t)crc; |
229 | } |
230 | |
231 | #if !defined(_KERNEL) && !defined(_BOOT) |
232 | static void |
233 | crom_desc_specver(uint32_t spec, uint32_t ver, char *buf, int len) |
234 | { |
235 | const char *s = NULL; |
236 | |
237 | if (spec == CSRVAL_ANSIT10 || spec == 0) { |
238 | switch (ver) { |
239 | case CSRVAL_T10SBP2: |
240 | s = "SBP-2" ; |
241 | break; |
242 | default: |
243 | if (spec != 0) |
244 | s = "unknown ANSIT10" ; |
245 | } |
246 | } |
247 | if (spec == CSRVAL_1394TA || spec == 0) { |
248 | switch (ver) { |
249 | case CSR_PROTAVC: |
250 | s = "AV/C" ; |
251 | break; |
252 | case CSR_PROTCAL: |
253 | s = "CAL" ; |
254 | break; |
255 | case CSR_PROTEHS: |
256 | s = "EHS" ; |
257 | break; |
258 | case CSR_PROTHAVI: |
259 | s = "HAVi" ; |
260 | break; |
261 | case CSR_PROTCAM104: |
262 | s = "1394 Cam 1.04" ; |
263 | break; |
264 | case CSR_PROTCAM120: |
265 | s = "1394 Cam 1.20" ; |
266 | break; |
267 | case CSR_PROTCAM130: |
268 | s = "1394 Cam 1.30" ; |
269 | break; |
270 | case CSR_PROTDPP: |
271 | s = "1394 Direct print" ; |
272 | break; |
273 | case CSR_PROTIICP: |
274 | s = "Industrial & Instrument" ; |
275 | break; |
276 | default: |
277 | if (spec != 0) |
278 | s = "unknown 1394TA" ; |
279 | } |
280 | } |
281 | if (s != NULL) |
282 | snprintf(buf, len, "%s" , s); |
283 | } |
284 | |
285 | const char * |
286 | crom_desc(struct crom_context *cc, char *buf, size_t len) |
287 | { |
288 | struct csrreg *reg; |
289 | struct csrdirectory *dir; |
290 | const char *desc; |
291 | uint16_t crc; |
292 | size_t l = 0; |
293 | |
294 | reg = crom_get(cc); |
295 | switch (reg->key & CSRTYPE_MASK) { |
296 | case CSRTYPE_I: |
297 | #if 0 |
298 | l += snprintf(buf + l, len - l, "0x%x" , reg->val); |
299 | #else |
300 | *buf = '\0'; |
301 | #endif |
302 | break; |
303 | case CSRTYPE_C: |
304 | l += snprintf(buf + l, len - l, "offset=0x%04x(%d)" , |
305 | reg->val, reg->val); |
306 | break; |
307 | case CSRTYPE_L: |
308 | /* XXX fall through */ |
309 | case CSRTYPE_D: |
310 | dir = (struct csrdirectory *) (reg + reg->val); |
311 | crc = crom_crc((uint32_t *)dir->entry, dir->crc_len); |
312 | l += snprintf(buf + l, len - l, "len=%d crc=0x%04x " , |
313 | dir->crc_len, crc); |
314 | |
315 | if (l > len) |
316 | l = len; |
317 | if (crc == dir->crc) |
318 | l += snprintf(buf + l, len - l, "(OK) " ); |
319 | else |
320 | l += snprintf(buf + l, len - l, "(NG, 0x%x) " , |
321 | dir->crc); |
322 | } |
323 | if (l > len) |
324 | l = len; |
325 | switch (reg->key) { |
326 | case CSRKEY_VENDOR: /* 0x03 */ |
327 | desc = "module_vendor_ID" ; |
328 | break; |
329 | case CSRKEY_HW: /* 0x04 */ |
330 | desc = "hardware_version" ; |
331 | break; |
332 | case CSRKEY_NCAP: /* 0x0c */ |
333 | desc = "node_capabilities" ; |
334 | break; |
335 | case CSRKEY_SPEC: /* 0x12 */ |
336 | desc = "unit_spec_ID" ; |
337 | break; |
338 | case CSRKEY_VER: /* 0x13 */ |
339 | desc = "unit_sw_version" ; |
340 | crom_desc_specver(0, reg->val, buf, len); |
341 | break; |
342 | case CSRKEY_DINFO: /* 0x14 */ |
343 | desc = "logical_unit_number" ; |
344 | break; |
345 | case CSRKEY_MODEL: /* 0x17 */ |
346 | desc = "model_ID" ; |
347 | break; |
348 | case CSRKEY_REV: /* 0x21 */ |
349 | desc = "revision_ID" ; |
350 | break; |
351 | case 0x38: |
352 | desc = "command_set_spec_ID" ; |
353 | break; |
354 | case 0x39: |
355 | desc = "command_set" ; |
356 | break; |
357 | case 0x3a: |
358 | desc = "unit_characteristics" ; |
359 | break; |
360 | case 0x3b: |
361 | desc = "command_set_revision" ; |
362 | break; |
363 | case 0x3c: |
364 | desc = "firmware_revision" ; |
365 | break; |
366 | case 0x3d: |
367 | desc = "reconnect_timeout" ; |
368 | break; |
369 | case 0x40: |
370 | desc = "command_regs_base" ; |
371 | break; |
372 | case 0x54: |
373 | desc = "management_agent" ; |
374 | break; |
375 | case CROM_TEXTLEAF: /* 0x81 */ |
376 | case CROM_TEXTLEAF2: /* 0x82 */ |
377 | desc = "text_leaf" ; |
378 | crom_parse_text(cc, buf + l, len - l); |
379 | break; |
380 | case CROM_NODEID: /* 0x8d */ |
381 | desc = "node_unique_ID" ; |
382 | break; |
383 | case 0xd1: |
384 | desc = "unit_directory" ; |
385 | break; |
386 | case 0xd4: |
387 | desc = "logical_unit_directory" ; |
388 | break; |
389 | default: |
390 | desc = "unknown" ; |
391 | } |
392 | return desc; |
393 | } |
394 | #endif |
395 | |
396 | #if defined(_KERNEL) || defined(_BOOT) || defined(TEST) |
397 | |
398 | int |
399 | crom_add_quad(struct crom_chunk *chunk, uint32_t entry) |
400 | { |
401 | int index; |
402 | |
403 | index = chunk->data.crc_len; |
404 | if (index >= CROM_MAX_CHUNK_LEN - 1) { |
405 | printf("too large chunk %d\n" , index); |
406 | return -1; |
407 | } |
408 | chunk->data.buf[index] = entry; |
409 | chunk->data.crc_len++; |
410 | return index; |
411 | } |
412 | |
413 | int |
414 | crom_add_entry(struct crom_chunk *chunk, int key, int val) |
415 | { |
416 | union { |
417 | struct csrreg reg; |
418 | uint32_t i; |
419 | } foo; |
420 | |
421 | foo.reg.key = key; |
422 | foo.reg.val = val; |
423 | |
424 | return crom_add_quad(chunk, foo.i); |
425 | } |
426 | |
427 | int |
428 | crom_add_chunk(struct crom_src *src, struct crom_chunk *parent, |
429 | struct crom_chunk *child, int key) |
430 | { |
431 | int index; |
432 | |
433 | if (parent == NULL) { |
434 | STAILQ_INSERT_TAIL(&src->chunk_list, child, link); |
435 | return 0; |
436 | } |
437 | |
438 | index = crom_add_entry(parent, key, 0); |
439 | if (index < 0) |
440 | return -1; |
441 | child->ref_chunk = parent; |
442 | child->ref_index = index; |
443 | STAILQ_INSERT_TAIL(&src->chunk_list, child, link); |
444 | return index; |
445 | } |
446 | |
447 | #define MAX_TEXT (int)((CROM_MAX_CHUNK_LEN + 1) * 4 - sizeof(struct csrtext)) |
448 | int |
449 | crom_add_simple_text(struct crom_src *src, struct crom_chunk *parent, |
450 | struct crom_chunk *chunk, const char *buf) |
451 | { |
452 | struct csrtext *tl; |
453 | uint32_t *p; |
454 | int len, i; |
455 | char t[MAX_TEXT]; |
456 | |
457 | len = strlen(buf); |
458 | if (len > MAX_TEXT) { |
459 | printf("text(%d) trancated to %d.\n" , len, MAX_TEXT); |
460 | len = MAX_TEXT; |
461 | } |
462 | |
463 | tl = (struct csrtext *) &chunk->data; |
464 | tl->crc_len = howmany(sizeof(struct csrtext) + len, sizeof(uint32_t)); |
465 | tl->spec_id = 0; |
466 | tl->spec_type = 0; |
467 | tl->lang_id = 0; |
468 | memset(t, 0, roundup2(len, sizeof(uint32_t))); |
469 | memcpy(t, buf, len); |
470 | p = (uint32_t *)t; |
471 | for (i = 0; i < howmany(len, sizeof(uint32_t)); i++) |
472 | tl->text[i] = ntohl(*p++); |
473 | return crom_add_chunk(src, parent, chunk, CROM_TEXTLEAF); |
474 | } |
475 | |
476 | static int |
477 | crom_copy(uint32_t *src, uint32_t *dst, int *offset, int len, int maxlen) |
478 | { |
479 | |
480 | if (*offset + len > maxlen) { |
481 | printf("Config. ROM is too large for the buffer\n" ); |
482 | return -1; |
483 | } |
484 | memcpy((char *)(dst + *offset), src, len * sizeof(uint32_t)); |
485 | *offset += len; |
486 | return 0; |
487 | } |
488 | |
489 | int |
490 | crom_load(struct crom_src *src, uint32_t *buf, int maxlen) |
491 | { |
492 | struct crom_chunk *chunk, *parent; |
493 | struct csrhdr *hdr; |
494 | #if defined(_KERNEL) || defined(_BOOT) |
495 | uint32_t *ptr; |
496 | int i; |
497 | #endif |
498 | int count, offset; |
499 | int len; |
500 | |
501 | offset = 0; |
502 | /* Determine offset */ |
503 | STAILQ_FOREACH(chunk, &src->chunk_list, link) { |
504 | chunk->offset = offset; |
505 | /* Assume the offset of the parent is already known */ |
506 | parent = chunk->ref_chunk; |
507 | if (parent != NULL) { |
508 | struct csrreg *reg; |
509 | const int ref_index = chunk->ref_index; |
510 | |
511 | reg = (struct csrreg *)&parent->data.buf[ref_index]; |
512 | reg->val = |
513 | offset - (parent->offset + 1 + chunk->ref_index); |
514 | } |
515 | offset += (1 + chunk->data.crc_len); |
516 | } |
517 | |
518 | /* Calculate CRC and dump to the buffer */ |
519 | len = 1 + src->hdr.info_len; |
520 | count = 0; |
521 | if (crom_copy((uint32_t *)&src->hdr, buf, &count, len, maxlen) < 0) |
522 | return -1; |
523 | STAILQ_FOREACH(chunk, &src->chunk_list, link) { |
524 | chunk->data.crc = |
525 | crom_crc(chunk->data.buf, chunk->data.crc_len); |
526 | |
527 | len = 1 + chunk->data.crc_len; |
528 | if (crom_copy((uint32_t *)&chunk->data, buf, &count, len, |
529 | maxlen) < 0) |
530 | return -1; |
531 | } |
532 | hdr = (struct csrhdr *)buf; |
533 | hdr->crc_len = count - 1; |
534 | hdr->crc = crom_crc(&buf[1], hdr->crc_len); |
535 | |
536 | #if defined(_KERNEL) || defined(_BOOT) |
537 | /* byte swap */ |
538 | ptr = buf; |
539 | for (i = 0; i < count; i++) { |
540 | *ptr = htonl(*ptr); |
541 | ptr++; |
542 | } |
543 | #endif |
544 | |
545 | return count; |
546 | } |
547 | #endif |
548 | |
549 | #ifdef TEST |
550 | int |
551 | main(void) |
552 | { |
553 | struct crom_src src; |
554 | struct crom_chunk root, unit[3], text[7]; |
555 | uint32_t buf[256], *p; |
556 | int i; |
557 | extern const char ostype[]; |
558 | |
559 | memset(&src, 0, sizeof(src)); |
560 | memset(&root, 0, sizeof(root)); |
561 | memset(unit, 0, sizeof(unit)); |
562 | memset(text, 0, sizeof(text)); |
563 | memset(buf, 0, sizeof(buf)); |
564 | |
565 | /* BUS info sample */ |
566 | src.hdr.info_len = 4; |
567 | src.businfo.bus_name = CSR_BUS_NAME_IEEE1394; |
568 | src.businfo.eui64.hi = 0x11223344; |
569 | src.businfo.eui64.lo = 0x55667788; |
570 | src.businfo.link_spd = FWSPD_S400; |
571 | src.businfo.generation = 0; |
572 | src.businfo.max_rom = MAXROM_4; |
573 | src.businfo.max_rec = 10; |
574 | src.businfo.cyc_clk_acc = 100; |
575 | src.businfo.pmc = 0; |
576 | src.businfo.bmc = 1; |
577 | src.businfo.isc = 1; |
578 | src.businfo.cmc = 1; |
579 | src.businfo.irmc = 1; |
580 | STAILQ_INIT(&src.chunk_list); |
581 | |
582 | /* Root directory */ |
583 | crom_add_chunk(&src, NULL, &root, 0); |
584 | crom_add_entry(&root, CSRKEY_NCAP, 0x123456); |
585 | /* private company_id */ |
586 | crom_add_entry(&root, CSRKEY_VENDOR, 0xacde48); |
587 | |
588 | crom_add_simple_text(&src, &root, &text[0], ostype); |
589 | crom_add_entry(&root, CSRKEY_HW, __NetBSD_Version__); |
590 | crom_add_simple_text(&src, &root, &text[1], OS_VER_STR); |
591 | |
592 | /* SBP unit directory */ |
593 | crom_add_chunk(&src, &root, &unit[0], CROM_UDIR); |
594 | crom_add_entry(&unit[0], CSRKEY_SPEC, CSRVAL_ANSIT10); |
595 | crom_add_entry(&unit[0], CSRKEY_VER, CSRVAL_T10SBP2); |
596 | crom_add_entry(&unit[0], CSRKEY_COM_SPEC, CSRVAL_ANSIT10); |
597 | crom_add_entry(&unit[0], CSRKEY_COM_SET, CSRVAL_SCSI); |
598 | /* management_agent */ |
599 | crom_add_entry(&unit[0], CROM_MGM, 0x1000); |
600 | crom_add_entry(&unit[0], CSRKEY_UNIT_CH, (10<<8) | 8); |
601 | /* Device type and LUN */ |
602 | crom_add_entry(&unit[0], CROM_LUN, 0); |
603 | crom_add_entry(&unit[0], CSRKEY_MODEL, 1); |
604 | crom_add_simple_text(&src, &unit[0], &text[2], "scsi_target" ); |
605 | |
606 | /* RFC2734 IPv4 over IEEE1394 */ |
607 | crom_add_chunk(&src, &root, &unit[1], CROM_UDIR); |
608 | crom_add_entry(&unit[1], CSRKEY_SPEC, CSRVAL_IETF); |
609 | crom_add_simple_text(&src, &unit[1], &text[3], "IANA" ); |
610 | crom_add_entry(&unit[1], CSRKEY_VER, 1); |
611 | crom_add_simple_text(&src, &unit[1], &text[4], "IPv4" ); |
612 | |
613 | /* RFC3146 IPv6 over IEEE1394 */ |
614 | crom_add_chunk(&src, &root, &unit[2], CROM_UDIR); |
615 | crom_add_entry(&unit[2], CSRKEY_SPEC, CSRVAL_IETF); |
616 | crom_add_simple_text(&src, &unit[2], &text[5], "IANA" ); |
617 | crom_add_entry(&unit[2], CSRKEY_VER, 2); |
618 | crom_add_simple_text(&src, &unit[2], &text[6], "IPv6" ); |
619 | |
620 | crom_load(&src, buf, 256); |
621 | p = buf; |
622 | #define DUMP_FORMAT "%08x %08x %08x %08x %08x %08x %08x %08x\n" |
623 | for (i = 0; i < 256 / 8; i++) { |
624 | printf(DUMP_FORMAT, |
625 | p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]); |
626 | p += 8; |
627 | } |
628 | return 0; |
629 | } |
630 | #endif |
631 | |