1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * Copyright (C) 1999-2002 Silicon Graphics, Inc. All rights reserved. 7 */ 8 9/* 10 * WARNING: There is more than one copy of this file in different isms. 11 * All copies must be kept exactly in sync. 12 * Do not modify this file without also updating the following: 13 * 14 * irix/kern/io/eeprom.c 15 * stand/arcs/lib/libsk/ml/eeprom.c 16 * stand/arcs/lib/libkl/io/eeprom.c 17 * 18 * (from time to time they might not be in sync but that's due to bringup 19 * activity - this comment is to remind us that they eventually have to 20 * get back together) 21 * 22 * eeprom.c 23 * 24 * access to board-mounted EEPROMs via the L1 system controllers 25 * 26 */ 27 28#include <linux/types.h> 29#include <linux/config.h> 30#include <linux/slab.h> 31#include <asm/sn/sgi.h> 32#include <asm/sn/io.h> 33#include <asm/sn/iograph.h> 34#include <asm/sn/invent.h> 35#include <asm/sn/hcl.h> 36#include <asm/sn/hcl_util.h> 37#include <asm/sn/labelcl.h> 38#include <asm/sn/eeprom.h> 39#include <asm/sn/router.h> 40#include <asm/sn/module.h> 41#include <asm/sn/ksys/l1.h> 42#include <asm/sn/nodepda.h> 43#include <asm/sn/clksupport.h> 44#include <asm/sn/sn_cpuid.h> 45#include <asm/sn/simulator.h> 46 47#if defined(EEPROM_DEBUG) 48#define db_printf(x) printk x 49#else 50#define db_printf(x) printk x 51#endif 52 53#define BCOPY(x,y,z) memcpy(y,x,z) 54 55#define UNDERSCORE 0 /* don't convert underscores to hyphens */ 56#define HYPHEN 1 /* convert underscores to hyphens */ 57 58void copy_ascii_field( char *to, char *from, int length, 59 int change_underscore ); 60uint64_t generate_unique_id( char *sn, int sn_len ); 61uchar_t char_to_base36( char c ); 62int nicify( char *dst, eeprom_brd_record_t *src ); 63static void int64_to_hex_string( char *out, uint64_t val ); 64 65// extern int router_lock( net_vec_t, int, int ); 66// extern int router_unlock( net_vec_t ); 67#define ROUTER_LOCK(p) // router_lock(p, 10000, 3000000) 68#define ROUTER_UNLOCK(p) // router_unlock(p) 69 70#define IP27LOG_OVNIC "OverrideNIC" 71 72 73/* the following function converts an EEPROM record to a close facsimile 74 * of the string returned by reading a Dallas Semiconductor NIC (see 75 * one of the many incarnations of nic.c for details on that driver) 76 */ 77int nicify( char *dst, eeprom_brd_record_t *src ) 78{ 79 int field_len; 80 uint64_t unique_id; 81 char *cur_dst = dst; 82 eeprom_board_ia_t *board; 83 84 board = src->board_ia; 85 ASSERT( board ); /* there should always be a board info area */ 86 87 /* copy part number */ 88 strcpy( cur_dst, "Part:" ); 89 cur_dst += strlen( cur_dst ); 90 ASSERT( (board->part_num_tl & FIELD_FORMAT_MASK) 91 == FIELD_FORMAT_ASCII ); 92 field_len = board->part_num_tl & FIELD_LENGTH_MASK; 93 copy_ascii_field( cur_dst, board->part_num, field_len, HYPHEN ); 94 cur_dst += field_len; 95 96 /* copy product name */ 97 strcpy( cur_dst, ";Name:" ); 98 cur_dst += strlen( cur_dst ); 99 ASSERT( (board->product_tl & FIELD_FORMAT_MASK) == FIELD_FORMAT_ASCII ); 100 field_len = board->product_tl & FIELD_LENGTH_MASK; 101 copy_ascii_field( cur_dst, board->product, field_len, UNDERSCORE ); 102 cur_dst += field_len; 103 104 /* copy serial number */ 105 strcpy( cur_dst, ";Serial:" ); 106 cur_dst += strlen( cur_dst ); 107 ASSERT( (board->serial_num_tl & FIELD_FORMAT_MASK) 108 == FIELD_FORMAT_ASCII ); 109 field_len = board->serial_num_tl & FIELD_LENGTH_MASK; 110 copy_ascii_field( cur_dst, board->serial_num, field_len, 111 HYPHEN); 112 113 cur_dst += field_len; 114 115 /* copy revision */ 116 strcpy( cur_dst, ";Revision:"); 117 cur_dst += strlen( cur_dst ); 118 ASSERT( (board->board_rev_tl & FIELD_FORMAT_MASK) 119 == FIELD_FORMAT_ASCII ); 120 field_len = board->board_rev_tl & FIELD_LENGTH_MASK; 121 copy_ascii_field( cur_dst, board->board_rev, field_len, HYPHEN ); 122 cur_dst += field_len; 123 124 /* EEPROMs don't have equivalents for the Group, Capability and 125 * Variety fields, so we pad these with 0's 126 */ 127 strcpy( cur_dst, ";Group:ff;Capability:ffffffff;Variety:ff" ); 128 cur_dst += strlen( cur_dst ); 129 130 /* use the board serial number to "fake" a laser id */ 131 strcpy( cur_dst, ";Laser:" ); 132 cur_dst += strlen( cur_dst ); 133 unique_id = generate_unique_id( board->serial_num, 134 board->serial_num_tl & FIELD_LENGTH_MASK ); 135 int64_to_hex_string( cur_dst, unique_id ); 136 strcat( dst, ";" ); 137 138 return 1; 139} 140 141 142/* These functions borrow heavily from chars2* in nic.c 143 */ 144void copy_ascii_field( char *to, char *from, int length, 145 int change_underscore ) 146{ 147 int i; 148 for( i = 0; i < length; i++ ) { 149 150 /* change underscores to hyphens if requested */ 151 if( from[i] == '_' && change_underscore == HYPHEN ) 152 to[i] = '-'; 153 154 /* ; and ; are separators, so mustn't appear within 155 * a field */ 156 else if( from[i] == ':' || from[i] == ';' ) 157 to[i] = '?'; 158 159 /* I'm not sure why or if ASCII character 0xff would 160 * show up in an EEPROM field, but the NIC parsing 161 * routines wouldn't like it if it did... so we 162 * get rid of it, just in case. */ 163 else if( (unsigned char)from[i] == (unsigned char)0xff ) 164 to[i] = ' '; 165 166 /* unprintable characters are replaced with . */ 167 else if( from[i] < ' ' || from[i] >= 0x7f ) 168 to[i] = '.'; 169 170 /* otherwise, just copy the character */ 171 else 172 to[i] = from[i]; 173 } 174 175 if( i == 0 ) { 176 to[i] = ' '; /* return at least a space... */ 177 i++; 178 } 179 to[i] = 0; /* terminating null */ 180} 181 182/* Note that int64_to_hex_string currently only has a big-endian 183 * implementation. 184 */ 185#ifdef _MIPSEB 186static void int64_to_hex_string( char *out, uint64_t val ) 187{ 188 int i; 189 uchar_t table[] = "0123456789abcdef"; 190 uchar_t *byte_ptr = (uchar_t *)&val; 191 for( i = 0; i < sizeof(uint64_t); i++ ) { 192 out[i*2] = table[ ((*byte_ptr) >> 4) & 0x0f ]; 193 out[i*2+1] = table[ (*byte_ptr) & 0x0f ]; 194 byte_ptr++; 195 } 196 out[i*2] = '\0'; 197} 198 199#else /* little endian */ 200 201static void int64_to_hex_string( char *out, uint64_t val ) 202{ 203 204 205 printk("int64_to_hex_string needs a little-endian implementation.\n"); 206} 207#endif /* _MIPSEB */ 208 209/* Convert a standard ASCII serial number to a unique integer 210 * id number by treating the serial number string as though 211 * it were a base 36 number 212 */ 213uint64_t generate_unique_id( char *sn, int sn_len ) 214{ 215 int uid = 0; 216 int i; 217 218 #define VALID_BASE36(c) ((c >= '0' && c <='9') \ 219 || (c >= 'A' && c <='Z') \ 220 || (c >= 'a' && c <='z')) 221 222 for( i = 0; i < sn_len; i++ ) { 223 if( !VALID_BASE36(sn[i]) ) 224 continue; 225 uid *= 36; 226 uid += char_to_base36( sn[i] ); 227 } 228 229 if( uid == 0 ) 230 return rtc_time(); 231 232 return uid; 233} 234 235uchar_t char_to_base36( char c ) 236{ 237 uchar_t val; 238 239 if( c >= '0' && c <= '9' ) 240 val = (c - '0'); 241 242 else if( c >= 'A' && c <= 'Z' ) 243 val = (c - 'A' + 10); 244 245 else if( c >= 'a' && c <= 'z' ) 246 val = (c - 'a' + 10); 247 248 else val = 0; 249 250 return val; 251} 252 253 254/* given a pointer to the three-byte little-endian EEPROM representation 255 * of date-of-manufacture, this function translates to a big-endian 256 * integer format 257 */ 258int eeprom_xlate_board_mfr_date( uchar_t *src ) 259{ 260 int rval = 0; 261 rval += *src; src++; 262 rval += ((int)(*src) << 8); src ++; 263 rval += ((int)(*src) << 16); 264 return rval; 265} 266 267 268int eeprom_str( char *nic_str, nasid_t nasid, int component ) 269{ 270 eeprom_brd_record_t eep; 271 eeprom_board_ia_t board; 272 eeprom_chassis_ia_t chassis; 273 int r; 274 275 if( (component & C_DIMM) == C_DIMM ) { 276 /* this function isn't applicable to DIMMs */ 277 return EEP_PARAM; 278 } 279 else { 280 eep.board_ia = &board; 281 eep.spd = NULL; 282 if( !(component & SUBORD_MASK) ) 283 eep.chassis_ia = &chassis; /* only main boards have a chassis 284 * info area */ 285 else 286 eep.chassis_ia = NULL; 287 } 288 289 switch( component & BRICK_MASK ) { 290 case C_BRICK: 291 r = cbrick_eeprom_read( &eep, nasid, component ); 292 break; 293 case IO_BRICK: 294 r = iobrick_eeprom_read( &eep, nasid, component ); 295 break; 296 default: 297 return EEP_PARAM; /* must be an invalid component */ 298 } 299 if( r ) 300 return r; 301 if( !nicify( nic_str, &eep ) ) 302 return EEP_NICIFY; 303 304 return EEP_OK; 305} 306 307int vector_eeprom_str( char *nic_str, nasid_t nasid, 308 int component, net_vec_t path ) 309{ 310 eeprom_brd_record_t eep; 311 eeprom_board_ia_t board; 312 eeprom_chassis_ia_t chassis; 313 int r; 314 315 eep.board_ia = &board; 316 if( !(component & SUBORD_MASK) ) 317 eep.chassis_ia = &chassis; /* only main boards have a chassis 318 * info area */ 319 else 320 eep.chassis_ia = NULL; 321 322 if( !(component & VECTOR) ) 323 return EEP_PARAM; 324 325 if( (r = vector_eeprom_read( &eep, nasid, path, component )) ) 326 return r; 327 328 if( !nicify( nic_str, &eep ) ) 329 return EEP_NICIFY; 330 331 return EEP_OK; 332} 333 334 335int is_iobrick( int nasid, int widget_num ) 336{ 337 uint32_t wid_reg; 338 int part_num, mfg_num; 339 340 /* Read the widget's WIDGET_ID register to get 341 * its part number and mfg number 342 */ 343 wid_reg = *(volatile int32_t *) 344 (NODE_SWIN_BASE( nasid, widget_num ) + WIDGET_ID); 345 346 part_num = (wid_reg & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT; 347 mfg_num = (wid_reg & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT; 348 349 /* Is this the "xbow part" of an XBridge? If so, this 350 * widget is definitely part of an I/O brick. 351 */ 352 if( part_num == XXBOW_WIDGET_PART_NUM && 353 mfg_num == XXBOW_WIDGET_MFGR_NUM ) 354 355 return 1; 356 357 /* Is this a "bridge part" of an XBridge? If so, once 358 * again, we know this widget is part of an I/O brick. 359 */ 360 if( part_num == XBRIDGE_WIDGET_PART_NUM && 361 mfg_num == XBRIDGE_WIDGET_MFGR_NUM ) 362 363 return 1; 364 365 return 0; 366} 367 368 369int cbrick_uid_get( nasid_t nasid, uint64_t *uid ) 370{ 371 char uid_str[32]; 372 char msg[BRL1_QSIZE]; 373 int subch, len; 374 l1sc_t sc; 375 l1sc_t *scp; 376 int local = (nasid == get_nasid()); 377 378 if ( IS_RUNNING_ON_SIMULATOR() ) 379 return EEP_L1; 380 381 /* If the promlog variable pointed to by IP27LOG_OVNIC is set, 382 * use that value for the cbrick UID rather than the EEPROM 383 * serial number. 384 */ 385#ifdef LOG_GETENV 386 if( ip27log_getenv( nasid, IP27LOG_OVNIC, uid_str, NULL, 0 ) >= 0 ) 387 { 388 /* We successfully read IP27LOG_OVNIC, so return it as the UID. */ 389 db_printf(( "cbrick_uid_get:" 390 "Overriding UID with environment variable %s\n", 391 IP27LOG_OVNIC )); 392 *uid = strtoull( uid_str, NULL, 0 ); 393 return EEP_OK; 394 } 395#endif 396 397 /* If this brick is retrieving its own uid, use the local l1sc_t to 398 * arbitrate access to the l1; otherwise, set up a new one. 399 */ 400 if( local ) { 401 scp = get_l1sc(); 402 } 403 else { 404 scp = ≻ 405 sc_init( &sc, nasid, BRL1_LOCALHUB_UART ); 406 } 407 408 /* fill in msg with the opcode & params */ 409 BZERO( msg, BRL1_QSIZE ); 410 if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 ) 411 return EEP_L1; 412 413 if( (len = sc_construct_msg( scp, subch, msg, BRL1_QSIZE, 414 L1_ADDR_TASK_GENERAL, 415 L1_REQ_SER_NUM, 0 )) < 0 ) 416 { 417 sc_close( scp, subch ); 418 return( EEP_L1 ); 419 } 420 421 /* send the request to the L1 */ 422 if( sc_command( scp, subch, msg, msg, &len ) ) { 423 sc_close( scp, subch ); 424 return( EEP_L1 ); 425 } 426 427 /* free up subchannel */ 428 sc_close(scp, subch); 429 430 /* check response */ 431 if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 ) 432 { 433 return( EEP_L1 ); 434 } 435 436 *uid = generate_unique_id( uid_str, strlen( uid_str ) ); 437 438 return EEP_OK; 439} 440 441 442int rbrick_uid_get( nasid_t nasid, net_vec_t path, uint64_t *uid ) 443{ 444 char uid_str[32]; 445 char msg[BRL1_QSIZE]; 446 int subch, len; 447 l1sc_t sc; 448 449 if ( IS_RUNNING_ON_SIMULATOR() ) 450 return EEP_L1; 451 452#define FAIL \ 453 { \ 454 *uid = rtc_time(); \ 455 printk( "rbrick_uid_get failed; using current time as uid\n" ); \ 456 return EEP_OK; \ 457 } 458 459 ROUTER_LOCK(path); 460 sc_init( &sc, nasid, path ); 461 462 /* fill in msg with the opcode & params */ 463 BZERO( msg, BRL1_QSIZE ); 464 if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) { 465 ROUTER_UNLOCK(path); 466 FAIL; 467 } 468 469 if( (len = sc_construct_msg( &sc, subch, msg, BRL1_QSIZE, 470 L1_ADDR_TASK_GENERAL, 471 L1_REQ_SER_NUM, 0 )) < 0 ) 472 { 473 ROUTER_UNLOCK(path); 474 sc_close( &sc, subch ); 475 FAIL; 476 } 477 478 /* send the request to the L1 */ 479 if( sc_command( &sc, subch, msg, msg, &len ) ) { 480 ROUTER_UNLOCK(path); 481 sc_close( &sc, subch ); 482 FAIL; 483 } 484 485 /* free up subchannel */ 486 ROUTER_UNLOCK(path); 487 sc_close(&sc, subch); 488 489 /* check response */ 490 if( sc_interpret_resp( msg, 2, L1_ARG_ASCII, uid_str ) < 0 ) 491 { 492 FAIL; 493 } 494 495 *uid = generate_unique_id( uid_str, strlen( uid_str ) ); 496 497 return EEP_OK; 498} 499 500int iobrick_uid_get( nasid_t nasid, uint64_t *uid ) 501{ 502 eeprom_brd_record_t eep; 503 eeprom_board_ia_t board; 504 eeprom_chassis_ia_t chassis; 505 int r; 506 507 eep.board_ia = &board; 508 eep.chassis_ia = &chassis; 509 eep.spd = NULL; 510 511 r = iobrick_eeprom_read( &eep, nasid, IO_BRICK ); 512 if( r != EEP_OK ) { 513 *uid = rtc_time(); 514 return r; 515 } 516 517 *uid = generate_unique_id( board.serial_num, 518 board.serial_num_tl & FIELD_LENGTH_MASK ); 519 520 return EEP_OK; 521} 522 523 524int ibrick_mac_addr_get( nasid_t nasid, char *eaddr ) 525{ 526 eeprom_brd_record_t eep; 527 eeprom_board_ia_t board; 528 eeprom_chassis_ia_t chassis; 529 int r; 530 char *tmp; 531 532 eep.board_ia = &board; 533 eep.chassis_ia = &chassis; 534 eep.spd = NULL; 535 536 r = iobrick_eeprom_read( &eep, nasid, IO_BRICK ); 537 if( (r != EEP_OK) || (board.mac_addr[0] == '\0') ) { 538 db_printf(( "ibrick_mac_addr_get: " 539 "Couldn't read MAC address from EEPROM\n" )); 540 return EEP_L1; 541 } 542 else { 543 /* successfully read info area */ 544 int ix; 545 tmp = board.mac_addr; 546 for( ix = 0; ix < (board.mac_addr_tl & FIELD_LENGTH_MASK); ix++ ) 547 { 548 *eaddr++ = *tmp++; 549 } 550 *eaddr = '\0'; 551 } 552 553 return EEP_OK; 554} 555 556 557/* 558 * eeprom_vertex_info_set 559 * 560 * Given a vertex handle, a component designation, a starting nasid 561 * and (in the case of a router) a vector path to the component, this 562 * function will read the EEPROM and attach the resulting information 563 * to the vertex in the same string format as that provided by the 564 * Dallas Semiconductor NIC drivers. If the vertex already has the 565 * string, this function just returns the string. 566 */ 567 568extern char *nic_vertex_info_get( devfs_handle_t ); 569extern void nic_vmc_check( devfs_handle_t, char * ); 570/* the following were lifted from nic.c - change later? */ 571#define MAX_INFO 2048 572#define NEWSZ(ptr,sz) ((ptr) = kern_malloc((sz))) 573#define DEL(ptr) (kern_free((ptr))) 574 575char *eeprom_vertex_info_set( int component, int nasid, devfs_handle_t v, 576 net_vec_t path ) 577{ 578 char *info_tmp; 579 int info_len; 580 char *info; 581 582 /* see if this vertex is already marked */ 583 info_tmp = nic_vertex_info_get(v); 584 if (info_tmp) return info_tmp; 585 586 /* get a temporary place for the data */ 587 NEWSZ(info_tmp, MAX_INFO); 588 if (!info_tmp) return NULL; 589 590 /* read the EEPROM */ 591 if( component & R_BRICK ) { 592 if( RBRICK_EEPROM_STR( info_tmp, nasid, path ) != EEP_OK ) 593 return NULL; 594 } 595 else { 596 if( eeprom_str( info_tmp, nasid, component ) != EEP_OK ) 597 return NULL; 598 } 599 600 /* allocate a smaller final place */ 601 info_len = strlen(info_tmp)+1; 602 NEWSZ(info, info_len); 603 if (info) { 604 strcpy(info, info_tmp); 605 DEL(info_tmp); 606 } else { 607 info = info_tmp; 608 } 609 610 /* add info to the vertex */ 611 hwgraph_info_add_LBL(v, INFO_LBL_NIC, 612 (arbitrary_info_t) info); 613 614 /* see if someone else got there first */ 615 info_tmp = nic_vertex_info_get(v); 616 if (info != info_tmp) { 617 DEL(info); 618 return info_tmp; 619 } 620 621 /* export the data */ 622 hwgraph_info_export_LBL(v, INFO_LBL_NIC, info_len); 623 624 /* trigger all matching callbacks */ 625 nic_vmc_check(v, info); 626 627 return info; 628} 629 630 631/********************************************************************* 632 * 633 * stubs for use until the Bedrock/L1 link is available 634 * 635 */ 636 637#include <asm/sn/nic.h> 638 639/* #define EEPROM_TEST */ 640 641/* fake eeprom reading functions (replace when the BR/L1 communication 642 * channel is in working order) 643 */ 644 645 646/* generate a charater in [0-9A-Z]; if an "extra" character is 647 * specified (such as '_'), include it as one of the possibilities. 648 */ 649char random_eeprom_ch( char extra ) 650{ 651 char ch; 652 int modval = 36; 653 if( extra ) 654 modval++; 655 656 ch = rtc_time() % modval; 657 658 if( ch < 10 ) 659 ch += '0'; 660 else if( ch >= 10 && ch < 36 ) 661 ch += ('A' - 10); 662 else 663 ch = extra; 664 665 return ch; 666} 667 668void fake_a_part_number( char *buf, int component ) 669{ 670 int i; 671 switch( component ) { 672 673 /* insert component-specific routines here */ 674 675 case C_BRICK: 676 strcpy( buf, "030-1266-001" ); 677 break; 678 default: 679 for( i = 0; i < 12; i++ ) { 680 if( i == 3 || i == 8 ) 681 buf[i] = '-'; 682 else 683 buf[i] = random_eeprom_ch(0); 684 } 685 } 686} 687 688 689/* create a six-character serial number */ 690void fake_a_serial_number( char *buf, uint64_t ser ) 691{ 692 int i; 693 static const char hexchars[] = "0123456789ABCDEF"; 694 695 if (ser) { 696 for( i = 5; i >=0; i-- ) { 697 buf[i] = hexchars[ser & 0xf]; 698 ser >>= 4; 699 } 700 } 701 else { 702 for( i = 0; i < 6; i++ ) 703 buf[i] = random_eeprom_ch(0); 704 } 705} 706 707 708void fake_a_product_name( uchar_t *format, char* buf, int component ) 709{ 710 switch( component & BRICK_MASK ) { 711 712 case C_BRICK: 713 if( component & SUBORD_MASK ) { 714 strcpy( buf, "C_BRICK_SUB" ); 715 *format = 0xCB; 716 } 717 else { 718 strcpy( buf, "IP35" ); 719 *format = 0xC4; 720 } 721 break; 722 723 case R_BRICK: 724 if( component & SUBORD_MASK ) { 725 strcpy( buf, "R_BRICK_SUB" ); 726 *format = 0xCB; 727 } 728 else { 729 strcpy( buf, "R_BRICK" ); 730 *format = 0xC7; 731 } 732 break; 733 734 case IO_BRICK: 735 if( component & SUBORD_MASK ) { 736 strcpy( buf, "IO_BRICK_SUB" ); 737 *format = 0xCC; 738 } 739 else { 740 strcpy( buf, "IO_BRICK" ); 741 *format = 0xC8; 742 } 743 break; 744 745 default: 746 strcpy( buf, "UNK_DEVICE" ); 747 *format = 0xCA; 748 } 749} 750 751 752 753int fake_an_eeprom_record( eeprom_brd_record_t *buf, int component, 754 uint64_t ser ) 755{ 756 eeprom_board_ia_t *board; 757 eeprom_chassis_ia_t *chassis; 758 int i, cs; 759 760 board = buf->board_ia; 761 chassis = buf->chassis_ia; 762 763 if( !(component & SUBORD_MASK) ) { 764 if( !chassis ) 765 return EEP_PARAM; 766 chassis->format = 0; 767 chassis->length = 5; 768 chassis->type = 0x17; 769 770 chassis->part_num_tl = 0xCC; 771 fake_a_part_number( chassis->part_num, component ); 772 chassis->serial_num_tl = 0xC6; 773 fake_a_serial_number( chassis->serial_num, ser ); 774 775 cs = chassis->format + chassis->length + chassis->type 776 + chassis->part_num_tl + chassis->serial_num_tl; 777 for( i = 0; i < (chassis->part_num_tl & FIELD_LENGTH_MASK); i++ ) 778 cs += chassis->part_num[i]; 779 for( i = 0; i < (chassis->serial_num_tl & FIELD_LENGTH_MASK); i++ ) 780 cs += chassis->serial_num[i]; 781 chassis->checksum = 256 - (cs % 256); 782 } 783 784 if( !board ) 785 return EEP_PARAM; 786 board->format = 0; 787 board->length = 10; 788 board->language = 0; 789 board->mfg_date = 1789200; /* noon, 5/26/99 */ 790 board->manuf_tl = 0xC3; 791 strcpy( board->manuf, "SGI" ); 792 793 fake_a_product_name( &(board->product_tl), board->product, component ); 794 795 board->serial_num_tl = 0xC6; 796 fake_a_serial_number( board->serial_num, ser ); 797 798 board->part_num_tl = 0xCC; 799 fake_a_part_number( board->part_num, component ); 800 801 board->board_rev_tl = 0xC2; 802 board->board_rev[0] = '0'; 803 board->board_rev[1] = '1'; 804 805 board->eeprom_size_tl = 0x01; 806 board->eeprom_size = 1; 807 808 board->temp_waiver_tl = 0xC2; 809 board->temp_waiver[0] = '0'; 810 board->temp_waiver[1] = '1'; 811 812 cs = board->format + board->length + board->language 813 + (board->mfg_date & 0xFF) 814 + (board->mfg_date & 0xFF00) 815 + (board->mfg_date & 0xFF0000) 816 + board->manuf_tl + board->product_tl + board->serial_num_tl 817 + board->part_num_tl + board->board_rev_tl 818 + board->board_rev[0] + board->board_rev[1] 819 + board->eeprom_size_tl + board->eeprom_size + board->temp_waiver_tl 820 + board->temp_waiver[0] + board->temp_waiver[1]; 821 for( i = 0; i < (board->manuf_tl & FIELD_LENGTH_MASK); i++ ) 822 cs += board->manuf[i]; 823 for( i = 0; i < (board->product_tl & FIELD_LENGTH_MASK); i++ ) 824 cs += board->product[i]; 825 for( i = 0; i < (board->serial_num_tl & FIELD_LENGTH_MASK); i++ ) 826 cs += board->serial_num[i]; 827 for( i = 0; i < (board->part_num_tl & FIELD_LENGTH_MASK); i++ ) 828 cs += board->part_num[i]; 829 830 board->checksum = 256 - (cs % 256); 831 832 return EEP_OK; 833} 834 835#define EEPROM_CHUNKSIZE 64 836 837#if defined(EEPROM_DEBUG) 838#define RETURN_ERROR \ 839{ \ 840 printk( "read_ia error return, component 0x%x, line %d" \ 841 ", address 0x%x, ia code 0x%x\n", \ 842 l1_compt, __LINE__, sc->subch[subch].target, ia_code ); \ 843 return EEP_L1; \ 844} 845 846#else 847#define RETURN_ERROR return(EEP_L1) 848#endif 849 850int read_ia( l1sc_t *sc, int subch, int l1_compt, 851 int ia_code, char *eep_record ) 852{ 853 char msg[BRL1_QSIZE]; /* message buffer */ 854 int len; /* number of bytes used in message buffer */ 855 int ia_len = EEPROM_CHUNKSIZE; /* remaining bytes in info area */ 856 int offset = 0; /* current offset into info area */ 857 858 if ( IS_RUNNING_ON_SIMULATOR() ) 859 return EEP_L1; 860 861 BZERO( msg, BRL1_QSIZE ); 862 863 /* retrieve EEPROM data in 64-byte chunks 864 */ 865 866 while( ia_len ) 867 { 868 /* fill in msg with opcode & params */ 869 if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE, 870 L1_ADDR_TASK_GENERAL, 871 L1_REQ_EEPROM, 8, 872 L1_ARG_INT, l1_compt, 873 L1_ARG_INT, ia_code, 874 L1_ARG_INT, offset, 875 L1_ARG_INT, ia_len )) < 0 ) 876 { 877 RETURN_ERROR; 878 } 879 880 /* send the request to the L1 */ 881 882 if( sc_command( sc, subch, msg, msg, &len ) ) { 883 RETURN_ERROR; 884 } 885 886 /* check response */ 887 if( sc_interpret_resp( msg, 5, 888 L1_ARG_INT, &ia_len, 889 L1_ARG_UNKNOWN, &len, eep_record ) < 0 ) 890 { 891 RETURN_ERROR; 892 } 893 894 if( ia_len > EEPROM_CHUNKSIZE ) 895 ia_len = EEPROM_CHUNKSIZE; 896 897 eep_record += EEPROM_CHUNKSIZE; 898 offset += EEPROM_CHUNKSIZE; 899 } 900 901 return EEP_OK; 902} 903 904 905int read_spd( l1sc_t *sc, int subch, int l1_compt, 906 eeprom_spd_u *spd ) 907{ 908 char msg[BRL1_QSIZE]; /* message buffer */ 909 int len; /* number of bytes used in message buffer */ 910 int resp; /* l1 response code */ 911 int spd_len = EEPROM_CHUNKSIZE; /* remaining bytes in spd record */ 912 int offset = 0; /* current offset into spd record */ 913 char *spd_p = spd->bytes; /* "thumb" for writing to spd */ 914 915 if ( IS_RUNNING_ON_SIMULATOR() ) 916 return EEP_L1; 917 918 BZERO( msg, BRL1_QSIZE ); 919 920 /* retrieve EEPROM data in 64-byte chunks 921 */ 922 923 while( spd_len ) 924 { 925 /* fill in msg with opcode & params */ 926 if( (len = sc_construct_msg( sc, subch, msg, BRL1_QSIZE, 927 L1_ADDR_TASK_GENERAL, 928 L1_REQ_EEPROM, 8, 929 L1_ARG_INT, l1_compt, 930 L1_ARG_INT, L1_EEP_SPD, 931 L1_ARG_INT, offset, 932 L1_ARG_INT, spd_len )) < 0 ) 933 { 934 return( EEP_L1 ); 935 } 936 937 /* send the request to the L1 */ 938 if( sc_command( sc, subch, msg, msg, &len ) ) { 939 return( EEP_L1 ); 940 } 941 942 /* check response */ 943 if( (resp = sc_interpret_resp( msg, 5, 944 L1_ARG_INT, &spd_len, 945 L1_ARG_UNKNOWN, &len, spd_p )) < 0 ) 946 { 947 /* 948 * translate l1 response code to eeprom.c error codes: 949 * The L1 response will be L1_RESP_NAVAIL if the spd 950 * can't be read (i.e. the spd isn't physically there). It will 951 * return L1_RESP_INVAL if the spd exists, but fails the checksum 952 * test because the eeprom wasn't programmed, programmed incorrectly, 953 * or corrupted. L1_RESP_NAVAIL indicates the eeprom is likely not present, 954 * whereas L1_RESP_INVAL indicates the eeprom is present, but the data is 955 * invalid. 956 */ 957 if(resp == L1_RESP_INVAL) { 958 resp = EEP_BAD_CHECKSUM; 959 } else { 960 resp = EEP_L1; 961 } 962 return( resp ); 963 } 964 965 if( spd_len > EEPROM_CHUNKSIZE ) 966 spd_len = EEPROM_CHUNKSIZE; 967 968 spd_p += EEPROM_CHUNKSIZE; 969 offset += EEPROM_CHUNKSIZE; 970 } 971 return EEP_OK; 972} 973 974 975int read_chassis_ia( l1sc_t *sc, int subch, int l1_compt, 976 eeprom_chassis_ia_t *ia ) 977{ 978 char eep_record[512]; /* scratch area for building up info area */ 979 char *eep_rec_p = eep_record; /* thumb for moving through eep_record */ 980 int checksum = 0; /* use to verify eeprom record checksum */ 981 int i; 982 983 /* Read in info area record from the L1. 984 */ 985 if( read_ia( sc, subch, l1_compt, L1_EEP_CHASSIS, eep_record ) 986 != EEP_OK ) 987 { 988 return EEP_L1; 989 } 990 991 /* Now we've got the whole info area. Transfer it to the data structure. 992 */ 993 994 eep_rec_p = eep_record; 995 ia->format = *eep_rec_p++; 996 ia->length = *eep_rec_p++; 997 if( ia->length == 0 ) { 998 /* since we're using 8*ia->length-1 as an array index later, make 999 * sure it's sane. 1000 */ 1001 db_printf(( "read_chassis_ia: eeprom length byte of ZERO\n" )); 1002 return EEP_L1; 1003 } 1004 ia->type = *eep_rec_p++; 1005 1006 ia->part_num_tl = *eep_rec_p++; 1007 1008 (void)BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) ); 1009 eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK); 1010 1011 ia->serial_num_tl = *eep_rec_p++; 1012 1013 BCOPY( eep_rec_p, ia->serial_num, 1014 (ia->serial_num_tl & FIELD_LENGTH_MASK) ); 1015 eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK); 1016 1017 ia->checksum = eep_record[(8 * ia->length) - 1]; 1018 1019 /* verify checksum */ 1020 eep_rec_p = eep_record; 1021 checksum = 0; 1022 for( i = 0; i < (8 * ia->length); i++ ) { 1023 checksum += *eep_rec_p++; 1024 } 1025 1026 if( (checksum & 0xff) != 0 ) 1027 { 1028 db_printf(( "read_chassis_ia: bad checksum\n" )); 1029 db_printf(( "read_chassis_ia: target 0x%x uart 0x%lx\n", 1030 sc->subch[subch].target, sc->uart )); 1031 return EEP_BAD_CHECKSUM; 1032 } 1033 1034 return EEP_OK; 1035} 1036 1037 1038int read_board_ia( l1sc_t *sc, int subch, int l1_compt, 1039 eeprom_board_ia_t *ia ) 1040{ 1041 char eep_record[512]; /* scratch area for building up info area */ 1042 char *eep_rec_p = eep_record; /* thumb for moving through eep_record */ 1043 int checksum = 0; /* running checksum total */ 1044 int i; 1045 1046 BZERO( ia, sizeof( eeprom_board_ia_t ) ); 1047 1048 /* Read in info area record from the L1. 1049 */ 1050 if( read_ia( sc, subch, l1_compt, L1_EEP_BOARD, eep_record ) 1051 != EEP_OK ) 1052 { 1053 db_printf(( "read_board_ia: error reading info area from L1\n" )); 1054 return EEP_L1; 1055 } 1056 1057 /* Now we've got the whole info area. Transfer it to the data structure. 1058 */ 1059 1060 eep_rec_p = eep_record; 1061 ia->format = *eep_rec_p++; 1062 ia->length = *eep_rec_p++; 1063 if( ia->length == 0 ) { 1064 /* since we're using 8*ia->length-1 as an array index later, make 1065 * sure it's sane. 1066 */ 1067 db_printf(( "read_board_ia: eeprom length byte of ZERO\n" )); 1068 return EEP_L1; 1069 } 1070 ia->language = *eep_rec_p++; 1071 1072 ia->mfg_date = eeprom_xlate_board_mfr_date( (uchar_t *)eep_rec_p ); 1073 eep_rec_p += 3; 1074 1075 ia->manuf_tl = *eep_rec_p++; 1076 1077 BCOPY( eep_rec_p, ia->manuf, (ia->manuf_tl & FIELD_LENGTH_MASK) ); 1078 eep_rec_p += (ia->manuf_tl & FIELD_LENGTH_MASK); 1079 1080 ia->product_tl = *eep_rec_p++; 1081 1082 BCOPY( eep_rec_p, ia->product, (ia->product_tl & FIELD_LENGTH_MASK) ); 1083 eep_rec_p += (ia->product_tl & FIELD_LENGTH_MASK); 1084 1085 ia->serial_num_tl = *eep_rec_p++; 1086 1087 BCOPY(eep_rec_p, ia->serial_num, (ia->serial_num_tl & FIELD_LENGTH_MASK)); 1088 eep_rec_p += (ia->serial_num_tl & FIELD_LENGTH_MASK); 1089 1090 ia->part_num_tl = *eep_rec_p++; 1091 1092 BCOPY( eep_rec_p, ia->part_num, (ia->part_num_tl & FIELD_LENGTH_MASK) ); 1093 eep_rec_p += (ia->part_num_tl & FIELD_LENGTH_MASK); 1094 1095 eep_rec_p++; /* we do not use the FRU file id */ 1096 1097 ia->board_rev_tl = *eep_rec_p++; 1098 1099 BCOPY( eep_rec_p, ia->board_rev, (ia->board_rev_tl & FIELD_LENGTH_MASK) ); 1100 eep_rec_p += (ia->board_rev_tl & FIELD_LENGTH_MASK); 1101 1102 ia->eeprom_size_tl = *eep_rec_p++; 1103 ia->eeprom_size = *eep_rec_p++; 1104 1105 ia->temp_waiver_tl = *eep_rec_p++; 1106 1107 BCOPY( eep_rec_p, ia->temp_waiver, 1108 (ia->temp_waiver_tl & FIELD_LENGTH_MASK) ); 1109 eep_rec_p += (ia->temp_waiver_tl & FIELD_LENGTH_MASK); 1110 1111 /* if there's more, we must be reading a main board; get 1112 * additional fields 1113 */ 1114 if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) { 1115 1116 ia->ekey_G_tl = *eep_rec_p++; 1117 BCOPY( eep_rec_p, (char *)&ia->ekey_G, 1118 ia->ekey_G_tl & FIELD_LENGTH_MASK ); 1119 eep_rec_p += (ia->ekey_G_tl & FIELD_LENGTH_MASK); 1120 1121 ia->ekey_P_tl = *eep_rec_p++; 1122 BCOPY( eep_rec_p, (char *)&ia->ekey_P, 1123 ia->ekey_P_tl & FIELD_LENGTH_MASK ); 1124 eep_rec_p += (ia->ekey_P_tl & FIELD_LENGTH_MASK); 1125 1126 ia->ekey_Y_tl = *eep_rec_p++; 1127 BCOPY( eep_rec_p, (char *)&ia->ekey_Y, 1128 ia->ekey_Y_tl & FIELD_LENGTH_MASK ); 1129 eep_rec_p += (ia->ekey_Y_tl & FIELD_LENGTH_MASK); 1130 1131 /* 1132 * need to get a couple more fields if this is an I brick 1133 */ 1134 if( ((unsigned char)*eep_rec_p != (unsigned char)EEPROM_EOF) ) { 1135 1136 ia->mac_addr_tl = *eep_rec_p++; 1137 BCOPY( eep_rec_p, ia->mac_addr, 1138 ia->mac_addr_tl & FIELD_LENGTH_MASK ); 1139 eep_rec_p += (ia->mac_addr_tl & FIELD_LENGTH_MASK); 1140 1141 ia->ieee1394_cfg_tl = *eep_rec_p++; 1142 BCOPY( eep_rec_p, ia->ieee1394_cfg, 1143 ia->ieee1394_cfg_tl & FIELD_LENGTH_MASK ); 1144 1145 } 1146 } 1147 1148 ia->checksum = eep_record[(ia->length * 8) - 1]; 1149 1150 /* verify checksum */ 1151 eep_rec_p = eep_record; 1152 checksum = 0; 1153 for( i = 0; i < (8 * ia->length); i++ ) { 1154 checksum += *eep_rec_p++; 1155 } 1156 1157 if( (checksum & 0xff) != 0 ) 1158 { 1159 db_printf(( "read_board_ia: bad checksum\n" )); 1160 db_printf(( "read_board_ia: target 0x%x uart 0x%lx\n", 1161 sc->subch[subch].target, sc->uart )); 1162 return EEP_BAD_CHECKSUM; 1163 } 1164 1165 return EEP_OK; 1166} 1167 1168 1169int _cbrick_eeprom_read( eeprom_brd_record_t *buf, l1sc_t *scp, 1170 int component ) 1171{ 1172 int r; 1173 uint64_t uid = 0; 1174#ifdef LOG_GETENV 1175 char uid_str[32]; 1176#endif 1177 int l1_compt, subch; 1178 1179 if ( IS_RUNNING_ON_SIMULATOR() ) 1180 return EEP_L1; 1181 1182 /* make sure we're targeting a cbrick */ 1183 if( !(component & C_BRICK) ) 1184 return EEP_PARAM; 1185 1186 /* If the promlog variable pointed to by IP27LOG_OVNIC is set, 1187 * use that value for the cbrick UID rather than the EEPROM 1188 * serial number. 1189 */ 1190#ifdef LOG_GETENV 1191 if( ip27log_getenv( scp->nasid, IP27LOG_OVNIC, uid_str, "0", 0 ) >= 0 ) 1192 { 1193 db_printf(( "_cbrick_eeprom_read: " 1194 "Overriding UID with environment variable %s\n", 1195 IP27LOG_OVNIC )); 1196 uid = strtoull( uid_str, NULL, 0 ); 1197 } 1198#endif 1199 1200 if( (subch = sc_open( scp, L1_ADDR_LOCAL )) < 0 ) 1201 return EEP_L1; 1202 1203 if((component & C_DIMM) == C_DIMM) { 1204 l1_compt = L1_EEP_DIMM(component & COMPT_MASK); 1205 r = read_spd(scp,subch,l1_compt, buf->spd); 1206 sc_close(scp,subch); 1207 return(r); 1208 } 1209 1210 switch( component ) 1211 { 1212 case C_BRICK: 1213 /* c-brick motherboard */ 1214 l1_compt = L1_EEP_NODE; 1215 r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia ); 1216 if( r != EEP_OK ) { 1217 sc_close( scp, subch ); 1218 db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" )); 1219 return fake_an_eeprom_record( buf, component, uid ); 1220 } 1221 if( uid ) { 1222 /* If IP27LOG_OVNIC is set, we want to put that value 1223 * in as our UID. */ 1224 fake_a_serial_number( buf->chassis_ia->serial_num, uid ); 1225 buf->chassis_ia->serial_num_tl = 6; 1226 } 1227 break; 1228 1229 case C_PIMM: 1230 /* one of the PIMM boards */ 1231 l1_compt = L1_EEP_PIMM( component & COMPT_MASK ); 1232 break; 1233 1234 default: 1235 /* unsupported board type */ 1236 sc_close( scp, subch ); 1237 return EEP_PARAM; 1238 } 1239 1240 r = read_board_ia( scp, subch, l1_compt, buf->board_ia ); 1241 sc_close( scp, subch ); 1242 if( r != EEP_OK ) 1243 { 1244 db_printf(( "_cbrick_eeprom_read: using a fake eeprom record\n" )); 1245 return fake_an_eeprom_record( buf, component, uid ); 1246 } 1247 return EEP_OK; 1248} 1249 1250 1251int cbrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid, 1252 int component ) 1253{ 1254 l1sc_t *scp; 1255 int local = (nasid == get_nasid()); 1256 1257 if ( IS_RUNNING_ON_SIMULATOR() ) 1258 return EEP_L1; 1259 1260 /* If this brick is retrieving its own uid, use the local l1sc_t to 1261 * arbitrate access to the l1; otherwise, set up a new one (prom) or 1262 * use an existing remote l1sc_t (kernel) 1263 */ 1264 if( local ) { 1265 scp = get_l1sc(); 1266 } 1267 else { 1268 scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc; 1269 } 1270 1271 return _cbrick_eeprom_read( buf, scp, component ); 1272} 1273 1274 1275int iobrick_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid, 1276 int component ) 1277{ 1278 int r; 1279 int l1_compt, subch; 1280 l1sc_t *scp; 1281 int local = (nasid == get_nasid()); 1282 1283 if ( IS_RUNNING_ON_SIMULATOR() ) 1284 return EEP_L1; 1285 1286 /* make sure we're talking to an applicable brick */ 1287 if( !(component & IO_BRICK) ) { 1288 return EEP_PARAM; 1289 } 1290 1291 /* If we're talking to this c-brick's attached io brick, use 1292 * the local l1sc_t; otherwise, set up a new one (prom) or 1293 * use an existing remote l1sc_t (kernel) 1294 */ 1295 if( local ) { 1296 scp = get_l1sc(); 1297 } 1298 else { 1299 scp = &NODEPDA( NASID_TO_COMPACT_NODEID(nasid) )->module->elsc; 1300 } 1301 1302 if( (subch = sc_open( scp, L1_ADDR_LOCALIO )) < 0 ) 1303 return EEP_L1; 1304 1305 1306 switch( component ) 1307 { 1308 case IO_BRICK: 1309 /* IO brick motherboard */ 1310 l1_compt = L1_EEP_LOGIC; 1311 r = read_chassis_ia( scp, subch, l1_compt, buf->chassis_ia ); 1312 1313 if( r != EEP_OK ) { 1314 sc_close( scp, subch ); 1315 /* 1316 * Whenever we no longer need to test on hardware 1317 * that does not have EEPROMS, then this can be removed. 1318 */ 1319 r = fake_an_eeprom_record( buf, component, rtc_time() ); 1320 return r; 1321 } 1322 break; 1323 1324 case IO_POWER: 1325 /* IO brick power board */ 1326 l1_compt = L1_EEP_POWER; 1327 break; 1328 1329 default: 1330 /* unsupported board type */ 1331 sc_close( scp, subch ); 1332 return EEP_PARAM; 1333 } 1334 1335 r = read_board_ia( scp, subch, l1_compt, buf->board_ia ); 1336 sc_close( scp, subch ); 1337 if( r != EEP_OK ) { 1338 return r; 1339 } 1340 return EEP_OK; 1341} 1342 1343 1344int vector_eeprom_read( eeprom_brd_record_t *buf, nasid_t nasid, 1345 net_vec_t path, int component ) 1346{ 1347 int r; 1348 uint64_t uid = 0; 1349 int l1_compt, subch; 1350 l1sc_t sc; 1351 1352 if ( IS_RUNNING_ON_SIMULATOR() ) 1353 return EEP_L1; 1354 1355 /* make sure we're targeting an applicable brick */ 1356 if( !(component & VECTOR) ) 1357 return EEP_PARAM; 1358 1359 switch( component & BRICK_MASK ) 1360 { 1361 case R_BRICK: 1362 ROUTER_LOCK( path ); 1363 sc_init( &sc, nasid, path ); 1364 1365 if( (subch = sc_open( &sc, L1_ADDR_LOCAL )) < 0 ) 1366 { 1367 db_printf(( "vector_eeprom_read: couldn't open subch\n" )); 1368 ROUTER_UNLOCK(path); 1369 return EEP_L1; 1370 } 1371 switch( component ) 1372 { 1373 case R_BRICK: 1374 /* r-brick motherboard */ 1375 l1_compt = L1_EEP_LOGIC; 1376 r = read_chassis_ia( &sc, subch, l1_compt, buf->chassis_ia ); 1377 if( r != EEP_OK ) { 1378 sc_close( &sc, subch ); 1379 ROUTER_UNLOCK( path ); 1380 printk( "vector_eeprom_read: couldn't get rbrick eeprom info;" 1381 " using current time as uid\n" ); 1382 uid = rtc_time(); 1383 db_printf(("vector_eeprom_read: using a fake eeprom record\n")); 1384 return fake_an_eeprom_record( buf, component, uid ); 1385 } 1386 break; 1387 1388 case R_POWER: 1389 /* r-brick power board */ 1390 l1_compt = L1_EEP_POWER; 1391 break; 1392 1393 default: 1394 /* unsupported board type */ 1395 sc_close( &sc, subch ); 1396 ROUTER_UNLOCK( path ); 1397 return EEP_PARAM; 1398 } 1399 r = read_board_ia( &sc, subch, l1_compt, buf->board_ia ); 1400 sc_close( &sc, subch ); 1401 ROUTER_UNLOCK( path ); 1402 if( r != EEP_OK ) { 1403 db_printf(( "vector_eeprom_read: using a fake eeprom record\n" )); 1404 return fake_an_eeprom_record( buf, component, uid ); 1405 } 1406 return EEP_OK; 1407 1408 case C_BRICK: 1409 sc_init( &sc, nasid, path ); 1410 return _cbrick_eeprom_read( buf, &sc, component ); 1411 1412 default: 1413 /* unsupported brick type */ 1414 return EEP_PARAM; 1415 } 1416} 1417