* Move function types to a separate line.
[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  * $DragonFly: src/sys/bus/firewire/fwcrom.c,v 1.7 2006/01/22 14:03:51 swildner Exp $
35  */
36
37 #ifndef __DragonFly__
38 #include <sys/cdefs.h>
39 __FBSDID("$FreeBSD: src/sys/dev/firewire/fwcrom.c,v 1.9 2003/10/02 04:06:55 simokawa Exp $");
40 #endif
41
42 #include <sys/param.h>
43 #if defined(_KERNEL) || defined(TEST)
44 #include <sys/queue.h>
45 #endif
46 #ifdef _KERNEL
47 #include <sys/systm.h>
48 #include <sys/kernel.h>
49 #else
50 #include <netinet/in.h>
51 #include <fcntl.h>
52 #include <stdio.h>
53 #include <err.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #endif
57
58 #ifdef __DragonFly__
59 #include "firewire.h"
60 #include "iec13213.h"
61 #else
62 #include <dev/firewire/firewire.h>
63 #include <dev/firewire/iec13213.h>
64 #endif
65
66 #define MAX_ROM (1024 - sizeof(u_int32_t) * 5)
67 #define CROM_END(cc) ((vm_offset_t)(cc)->stack[0].dir + MAX_ROM - 1)
68
69 void
70 crom_init_context(struct crom_context *cc, u_int32_t *p)
71 {
72         struct csrhdr *hdr;
73
74         hdr = (struct csrhdr *)p;
75         if (hdr->info_len == 0) {
76                 /* 
77                  * This isn't supposed to happen but it does.   The problem
78                  * is possibly related to fw_attach_dev()'s going from an
79                  * FWDEVINIT to a FWDEVATTACHED state, or in a host<->host
80                  * situation where one host gets the root directory for
81                  * another before the other has actually initialized it.
82                  */
83                 printf("crom_init_context: WARNING, info_len is 0\n");
84                 cc->depth = -1;
85                 return;
86         }
87         if (hdr->info_len == 1) {
88                 /* minimum ROM */
89                 cc->depth = -1;
90         }
91         p += 1 + hdr->info_len;
92
93         /* check size of root directory */
94         if (((struct csrdirectory *)p)->crc_len == 0) {
95                 cc->depth = -1;
96                 return;
97         }
98         cc->depth = 0;
99         cc->stack[0].dir = (struct csrdirectory *)p;
100         cc->stack[0].index = 0;
101 }
102
103 struct csrreg *
104 crom_get(struct crom_context *cc)
105 {
106         struct crom_ptr *ptr;
107
108         ptr = &cc->stack[cc->depth];
109         return (&ptr->dir->entry[ptr->index]);
110 }
111
112 void
113 crom_next(struct crom_context *cc)
114 {
115         struct crom_ptr *ptr;
116         struct csrreg *reg;
117
118         if (cc->depth < 0)
119                 return;
120         reg = crom_get(cc);
121         if ((reg->key & CSRTYPE_MASK) == CSRTYPE_D) {
122                 if (cc->depth >= CROM_MAX_DEPTH) {
123                         printf("crom_next: too deep\n");
124                         goto again;
125                 }
126                 cc->depth ++;
127
128                 ptr = &cc->stack[cc->depth];
129                 ptr->dir = (struct csrdirectory *) (reg + reg->val);
130                 ptr->index = 0;
131                 goto check;
132         }
133 again:
134         ptr = &cc->stack[cc->depth];
135         ptr->index ++;
136 check:
137         if (ptr->index < ptr->dir->crc_len &&
138                         (vm_offset_t)crom_get(cc) <= CROM_END(cc))
139                 return;
140
141         if (ptr->index < ptr->dir->crc_len)
142                 printf("crom_next: bound check failed\n");
143
144         if (cc->depth > 0) {
145                 cc->depth--;
146                 goto again;
147         }
148         /* no more data */
149         cc->depth = -1;
150 }
151
152
153 struct csrreg *
154 crom_search_key(struct crom_context *cc, u_int8_t key)
155 {
156         struct csrreg *reg;
157
158         while(cc->depth >= 0) {
159                 reg = crom_get(cc);
160                 if (reg->key == key)
161                         return reg;
162                 crom_next(cc);
163         }
164         return NULL;
165 }
166
167 int
168 crom_has_specver(u_int32_t *p, u_int32_t spec, u_int32_t ver)
169 {
170         struct csrreg *reg;
171         struct crom_context c, *cc;
172         int state = 0;
173
174         cc = &c;
175         crom_init_context(cc, p);
176         while(cc->depth >= 0) {
177                 reg = crom_get(cc);
178                 if (state == 0) {
179                         if (reg->key == CSRKEY_SPEC && reg->val == spec)
180                                 state = 1;
181                         else
182                                 state = 0;
183                 } else {
184                         if (reg->key == CSRKEY_VER && reg->val == ver)
185                                 return 1;
186                         else
187                                 state = 0;
188                 }
189                 crom_next(cc);
190         }
191         return 0;
192 }
193
194 void
195 crom_parse_text(struct crom_context *cc, char *buf, int len)
196 {
197         struct csrreg *reg;
198         struct csrtext *textleaf;
199         u_int32_t *bp;
200         int i, qlen;
201         static char *nullstr = "(null)";
202
203         if (cc->depth < 0)
204                 return;
205
206         reg = crom_get(cc);
207         if (reg->key != CROM_TEXTLEAF ||
208                         (vm_offset_t)(reg + reg->val) > CROM_END(cc)) {
209                 strncpy(buf, nullstr, len);
210                 return;
211         }
212         textleaf = (struct csrtext *)(reg + reg->val);
213
214         if ((vm_offset_t)textleaf + textleaf->crc_len > CROM_END(cc)) {
215                 strncpy(buf, nullstr, len);
216                 return;
217         }
218
219         /* XXX should check spec and type */
220
221         bp = (u_int32_t *)&buf[0];
222         qlen = textleaf->crc_len - 2;
223         if (len < qlen * 4)
224                 qlen = len/4;
225         for (i = 0; i < qlen; i ++)
226                 *bp++ = ntohl(textleaf->text[i]);
227         /* make sure to terminate the string */
228         if (len <= qlen * 4)
229                 buf[len - 1] = 0;
230         else
231                 buf[qlen * 4] = 0;
232 }
233
234 u_int16_t
235 crom_crc(u_int32_t *ptr, int len)
236 {
237         int i, shift;
238         u_int32_t data, sum, crc = 0;
239
240         for (i = 0; i < len; i++) {
241                 data = ptr[i];
242                 for (shift = 28; shift >= 0; shift -= 4) {
243                         sum = ((crc >> 12) ^ (data >> shift)) & 0xf;
244                         crc = (crc << 4) ^ (sum << 12) ^ (sum << 5) ^ sum;
245                 }
246                 crc &= 0xffff;
247         }
248         return((u_int16_t) crc);
249 }
250
251 #ifndef _KERNEL
252 static void
253 crom_desc_specver(u_int32_t spec, u_int32_t ver, char *buf, int len)
254 {
255         char *s = NULL;
256
257         if (spec == CSRVAL_ANSIT10 || spec == 0) {
258                 switch (ver) {
259                 case CSRVAL_T10SBP2:
260                         s = "SBP-2";
261                         break;
262                 default:
263                         if (spec != 0)
264                                 s = "unknown ANSIT10";
265                 }
266         }
267         if (spec == CSRVAL_1394TA || spec == 0) {
268                 switch (ver) {
269                 case CSR_PROTAVC:
270                         s = "AV/C";
271                         break;
272                 case CSR_PROTCAL:
273                         s = "CAL";
274                         break;
275                 case CSR_PROTEHS:
276                         s = "EHS";
277                         break;
278                 case CSR_PROTHAVI:
279                         s = "HAVi";
280                         break;
281                 case CSR_PROTCAM104:
282                         s = "1394 Cam 1.04";
283                         break;
284                 case CSR_PROTCAM120:
285                         s = "1394 Cam 1.20";
286                         break;
287                 case CSR_PROTCAM130:
288                         s = "1394 Cam 1.30";
289                         break;
290                 case CSR_PROTDPP:
291                         s = "1394 Direct print";
292                         break;
293                 case CSR_PROTIICP:
294                         s = "Industrial & Instrument";
295                         break;
296                 default:
297                         if (spec != 0)
298                                 s = "unknown 1394TA";
299                 }
300         }
301         if (s != NULL)
302                 snprintf(buf, len, "%s", s);
303 }
304
305 char *
306 crom_desc(struct crom_context *cc, char *buf, int len)
307 {
308         struct csrreg *reg;
309         struct csrdirectory *dir;
310         char *desc, st;
311         u_int16_t crc;
312
313         reg = crom_get(cc);
314         switch (reg->key & CSRTYPE_MASK) {
315         case CSRTYPE_I:
316 #if 0
317                 len -= snprintf(buf, len, "%d", reg->val);
318                 buf += strlen(buf);
319 #else
320                 *buf = '\0';
321 #endif
322                 break;
323         case CSRTYPE_C:
324                 len -= snprintf(buf, len, "offset=0x%04x(%d)",
325                                                 reg->val, reg->val);
326                 buf += strlen(buf);
327                 break;
328         case CSRTYPE_L:
329                 /* XXX fall through */
330         case CSRTYPE_D:
331                 dir = (struct csrdirectory *) (reg + reg->val);
332                 crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len);
333                 len -= snprintf(buf, len, "len=%d crc=0x%04x(%s) ",
334                         dir->crc_len, dir->crc,
335                         (crc == dir->crc) ? "OK" : "NG");
336                 buf += strlen(buf);
337         }
338         switch (reg->key) {
339         case 0x03:
340                 desc = "module_vendor_ID";
341                 break;
342         case 0x04:
343                 desc = "hardware_version";
344                 break;
345         case 0x0c:
346                 desc = "node_capabilities";
347                 break;
348         case 0x12:
349                 desc = "unit_spec_ID";
350                 break;
351         case 0x13:
352                 desc = "unit_sw_version";
353                 crom_desc_specver(0, reg->val, buf, len);
354                 break;
355         case 0x14:
356                 desc = "logical_unit_number";
357                 break;
358         case 0x17:
359                 desc = "model_ID";
360                 break;
361         case 0x38:
362                 desc = "command_set_spec_ID";
363                 break;
364         case 0x39:
365                 desc = "command_set";
366                 break;
367         case 0x3a:
368                 desc = "unit_characteristics";
369                 break;
370         case 0x3b:
371                 desc = "command_set_revision";
372                 break;
373         case 0x3c:
374                 desc = "firmware_revision";
375                 break;
376         case 0x3d:
377                 desc = "reconnect_timeout";
378                 break;
379         case 0x54:
380                 desc = "management_agent";
381                 break;
382         case 0x81:
383                 desc = "text_leaf";
384                 crom_parse_text(cc, buf + strlen(buf), len);
385                 break;
386         case 0xd1:
387                 desc = "unit_directory";
388                 break;
389         case 0xd4:
390                 desc = "logical_unit_directory";
391                 break;
392         default:
393                 desc = "unknown";
394         }
395         return desc;
396 }
397 #endif
398
399 #if defined(_KERNEL) || defined(TEST)
400
401 int
402 crom_add_quad(struct crom_chunk *chunk, u_int32_t entry)
403 {
404         int index;
405
406         index = chunk->data.crc_len;
407         if (index >= CROM_MAX_CHUNK_LEN - 1) {
408                 printf("too large chunk %d\n", index);
409                 return(-1);
410         }
411         chunk->data.buf[index] = entry;
412         chunk->data.crc_len++;
413         return(index);
414 }
415
416 int
417 crom_add_entry(struct crom_chunk *chunk, int key, int val)
418 {
419         struct csrreg *reg;
420         u_int32_t i;
421         
422         reg = (struct csrreg *)&i;
423         reg->key = key;
424         reg->val = val;
425         return(crom_add_quad(chunk, (u_int32_t) i));
426 }
427
428 int
429 crom_add_chunk(struct crom_src *src, struct crom_chunk *parent,
430                                 struct crom_chunk *child, int key)
431 {
432         int index;
433
434         if (parent == NULL) {
435                 STAILQ_INSERT_TAIL(&src->chunk_list, child, link);
436                 return(0);
437         }
438
439         index = crom_add_entry(parent, key, 0);
440         if (index < 0) {
441                 return(-1);
442         }
443         child->ref_chunk = parent;
444         child->ref_index = index;
445         STAILQ_INSERT_TAIL(&src->chunk_list, child, link);
446         return(index);
447 }
448
449 #define MAX_TEXT ((CROM_MAX_CHUNK_LEN + 1) * 4 - sizeof(struct csrtext))
450 int
451 crom_add_simple_text(struct crom_src *src, struct crom_chunk *parent,
452                                 struct crom_chunk *chunk, char *buf)
453 {
454         struct csrtext *tl;
455         u_int32_t *p;
456         int len, i;
457         char t[MAX_TEXT];
458
459         len = strlen(buf);
460         if (len > MAX_TEXT) {
461 #if defined(__DragonFly__) || __FreeBSD_version < 500000
462                 printf("text(%d) trancated to %d.\n", len, MAX_TEXT);
463 #else
464                 printf("text(%d) trancated to %td.\n", len, MAX_TEXT);
465 #endif
466                 len = MAX_TEXT;
467         }
468
469         tl = (struct csrtext *) &chunk->data;
470         tl->crc_len = howmany(sizeof(struct csrtext) + len, sizeof(u_int32_t));
471         tl->spec_id = 0;
472         tl->spec_type = 0;
473         tl->lang_id = 0;
474         bzero(&t[0], roundup2(len, sizeof(u_int32_t)));
475         bcopy(buf, &t[0], len);
476         p = (u_int32_t *)&t[0];
477         for (i = 0; i < howmany(len, sizeof(u_int32_t)); i ++)
478                 tl->text[i] = ntohl(*p++);
479         return (crom_add_chunk(src, parent, chunk, CROM_TEXTLEAF));
480 }
481
482 static int
483 crom_copy(u_int32_t *src, u_int32_t *dst, int *offset, int len, int maxlen)
484 {
485         if (*offset + len > maxlen) {
486                 printf("Config. ROM is too large for the buffer\n");
487                 return(-1);
488         }
489         bcopy(src, (char *)(dst + *offset), len * sizeof(u_int32_t));
490         *offset += len;
491         return(0);
492 }
493
494 int
495 crom_load(struct crom_src *src, u_int32_t *buf, int maxlen)
496 {
497         struct crom_chunk *chunk, *parent;
498         struct csrhdr *hdr;
499 #ifdef _KERNEL
500         u_int32_t *ptr;
501         int i;
502 #endif
503         int count, offset;
504         int len;
505
506         offset = 0;
507         /* Determine offset */
508         STAILQ_FOREACH(chunk, &src->chunk_list, link) {
509                 chunk->offset = offset;
510                 /* Assume the offset of the parent is already known */
511                 parent = chunk->ref_chunk;
512                 if (parent != NULL) {
513                         struct csrreg *reg;
514                         reg = (struct csrreg *)
515                                 &parent->data.buf[chunk->ref_index];
516                         reg->val = offset -
517                                 (parent->offset + 1 + chunk->ref_index);
518                 }
519                 offset += 1 + chunk->data.crc_len;
520         }
521
522         /* Calculate CRC and dump to the buffer */
523         len = 1 + src->hdr.info_len;
524         count = 0;
525         if (crom_copy((u_int32_t *)&src->hdr, buf, &count, len, maxlen) < 0)
526                 return(-1);
527         STAILQ_FOREACH(chunk, &src->chunk_list, link) {
528                 chunk->data.crc =
529                         crom_crc(&chunk->data.buf[0], chunk->data.crc_len);
530
531                 len = 1 + chunk->data.crc_len;
532                 if (crom_copy((u_int32_t *)&chunk->data, buf,
533                                         &count, len, maxlen) < 0)
534                         return(-1);
535         }
536         hdr = (struct csrhdr *)buf;
537         hdr->crc_len = count - 1;
538         hdr->crc = crom_crc(&buf[1], hdr->crc_len);
539
540 #ifdef _KERNEL
541         /* byte swap */
542         ptr = buf;
543         for (i = 0; i < count; i ++) {
544                 *ptr = htonl(*ptr);
545                 ptr++;
546         }
547 #endif
548
549         return(count);
550 }
551 #endif
552
553 #ifdef TEST
554 int
555 main(int argc, char *argv[])
556 {
557         struct crom_src src;
558         struct crom_chunk root,unit1,unit2,unit3;
559         struct crom_chunk text1,text2,text3,text4,text5,text6,text7;
560         u_int32_t buf[256], *p;
561         int i;
562
563         bzero(&src, sizeof(src));
564         bzero(&root, sizeof(root));
565         bzero(&unit1, sizeof(unit1));
566         bzero(&unit2, sizeof(unit2));
567         bzero(&unit3, sizeof(unit3));
568         bzero(&text1, sizeof(text1));
569         bzero(&text2, sizeof(text2));
570         bzero(&text3, sizeof(text3));
571         bzero(&text3, sizeof(text4));
572         bzero(&text3, sizeof(text5));
573         bzero(&text3, sizeof(text6));
574         bzero(&text3, sizeof(text7));
575         bzero(buf, sizeof(buf));
576
577         /* BUS info sample */
578         src.hdr.info_len = 4;
579         src.businfo.bus_name = CSR_BUS_NAME_IEEE1394;
580         src.businfo.eui64.hi = 0x11223344;
581         src.businfo.eui64.lo = 0x55667788;
582         src.businfo.link_spd = FWSPD_S400;
583         src.businfo.generation = 0;
584         src.businfo.max_rom = MAXROM_4;
585         src.businfo.max_rec = 10;
586         src.businfo.cyc_clk_acc = 100;
587         src.businfo.pmc = 0;
588         src.businfo.bmc = 1;
589         src.businfo.isc = 1;
590         src.businfo.cmc = 1;
591         src.businfo.irmc = 1;
592         STAILQ_INIT(&src.chunk_list);
593
594         /* Root directory */
595         crom_add_chunk(&src, NULL, &root, 0);
596         crom_add_entry(&root, CSRKEY_NCAP, 0x123456);
597         /* private company_id */
598         crom_add_entry(&root, CSRKEY_VENDOR, 0xacde48);
599
600 #ifdef __DragonFly__
601         crom_add_simple_text(&src, &root, &text1, "DragonFly");
602         crom_add_entry(&root, CSRKEY_HW, __DragonFly_cc_version);
603         crom_add_simple_text(&src, &root, &text2, "DragonFly-1");
604 #else
605         crom_add_simple_text(&src, &root, &text1, "FreeBSD");
606         crom_add_entry(&root, CSRKEY_HW, __FreeBSD_version);
607         crom_add_simple_text(&src, &root, &text2, "FreeBSD-5");
608 #endif
609
610         /* SBP unit directory */
611         crom_add_chunk(&src, &root, &unit1, CROM_UDIR);
612         crom_add_entry(&unit1, CSRKEY_SPEC, CSRVAL_ANSIT10);
613         crom_add_entry(&unit1, CSRKEY_VER, CSRVAL_T10SBP2);
614         crom_add_entry(&unit1, CSRKEY_COM_SPEC, CSRVAL_ANSIT10);
615         crom_add_entry(&unit1, CSRKEY_COM_SET, CSRVAL_SCSI);
616         /* management_agent */
617         crom_add_entry(&unit1, CROM_MGM, 0x1000);
618         crom_add_entry(&unit1, CSRKEY_UNIT_CH, (10<<8) | 8);
619         /* Device type and LUN */
620         crom_add_entry(&unit1, CROM_LUN, 0);
621         crom_add_entry(&unit1, CSRKEY_MODEL, 1);
622         crom_add_simple_text(&src, &unit1, &text3, "scsi_target");
623
624         /* RFC2734 IPv4 over IEEE1394 */
625         crom_add_chunk(&src, &root, &unit2, CROM_UDIR);
626         crom_add_entry(&unit2, CSRKEY_SPEC, CSRVAL_IETF);
627         crom_add_simple_text(&src, &unit2, &text4, "IANA");
628         crom_add_entry(&unit2, CSRKEY_VER, 1);
629         crom_add_simple_text(&src, &unit2, &text5, "IPv4");
630
631         /* RFC3146 IPv6 over IEEE1394 */
632         crom_add_chunk(&src, &root, &unit3, CROM_UDIR);
633         crom_add_entry(&unit3, CSRKEY_SPEC, CSRVAL_IETF);
634         crom_add_simple_text(&src, &unit3, &text6, "IANA");
635         crom_add_entry(&unit3, CSRKEY_VER, 2);
636         crom_add_simple_text(&src, &unit3, &text7, "IPv6");
637
638         crom_load(&src, buf, 256);
639         p = buf;
640 #define DUMP_FORMAT     "%08x %08x %08x %08x %08x %08x %08x %08x\n"
641         for (i = 0; i < 256/8; i ++) {
642                 printf(DUMP_FORMAT,
643                         p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
644                 p += 8;
645         }
646         return(0);
647 }
648 #endif