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