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