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