1#!/usr/bin/perl
2
3# test_fru
4#
5# Test of the FRU code.
6#
7# Author: MontaVista Software, Inc.
8#         Corey Minyard <minyard@mvista.com>
9#         source@mvista.com
10#
11# Copyright 2004 MontaVista Software Inc.
12#
13#  This program is free software; you can redistribute it and/or
14#  modify it under the terms of the GNU Lesser General Public License
15#  as published by the Free Software Foundation; either version 2 of
16#  the License, or (at your option) any later version.
17#
18#
19#  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
20#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22#  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23#  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24#  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
25#  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26#  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27#  TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
28#  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29#
30#  You should have received a copy of the GNU Lesser General Public
31#  License along with this program; if not, write to the Free
32#  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33#
34
35use Lanserv;
36use OpenIPMI;
37
38my $errcountholder : shared = 0;
39$errcount = \$errcountholder;
40
41my $fru_field_table = {};
42
43sub reg_err {
44    my $str = shift;
45
46    $$errcount++;
47    print STDERR "***", $str, "\n";
48}
49
50sub get_errcount {
51    return $$errcount;
52}
53
54# Check a data field from the fru.  Parameters are:
55#  fru - where to get the data from
56#  name - The name of the field to check
57#  num - The number of the field to check, ignored if the field doesn't
58#    support numbers.
59#  exp_type - The expected type (integer, time, ascii, binary, unicode).
60#  exp_val - The expected value.  For integers and ASCII strings, this
61#    is the value.  For binary and unicode, this is a string with the
62#    values listed out.  If the value is expected to not be there, this
63#    should be undefined.
64sub check_fru_data {
65    my $fru = shift;
66    my $name = shift;
67    my $num = shift;
68    my $exp_type = shift;
69    my $exp_val = shift;
70    my $idx = $fru_field_table->{$name};
71    my ($tname, $type, $val);
72    my $tnum = $num;
73
74    if (!defined $idx) {
75	main::reg_err("Invalid name in check_fru_data: $name");
76	return;
77    }
78
79    ($tname, $type, $val) = split /\s+/, $fru->get($idx, \$tnum), 3;
80
81    if (!defined $tname) {
82	main::reg_err("Internal error on: $name [$num]");
83	return;
84    }
85
86    if (!defined $type) {
87	if (defined $exp_val) {
88	    main::reg_err("value for $name [$num] was undefined, expected $exp_val");
89	}
90	return;
91    }
92
93    if ($name ne $tname) {
94	main::reg_err("Internal error on: $name [$num], $tname");
95	return;
96    }
97
98    if ($exp_type ne $type) {
99	main::reg_err("Type mismatch on $name [$num], expected $exp_type, got $type");
100	return;
101    }
102
103    # If the length of the thing is zero, there won't be anything in the string.  That's ok,
104    # but make the comparisons work.
105    if (!defined $val) {
106	$val = "";
107    }
108
109    if (($exp_type eq "integer") || ($exp_type eq "time")) {
110	if ($exp_val != $val) {
111	    main::reg_err("Value mismatch on $name [$num], expected $exp_val, got $val");
112	    return;
113	}
114    } elsif ($exp_type eq "ascii") {
115	if ($exp_val ne $val) {
116	    main::reg_err("Value mismatch on $name [$num], expected '$exp_val', got '$val'");
117	    return;
118	}
119    } elsif (($exp_type ne "binary") || ($exp_type ne "unicode")) {
120	my @vals = split /\s+/, $val;
121	my @exp_vals = split /\s+/, $exp_val;
122	my $i;
123	if ($#vals != $#exp_vals) {
124	    main::reg_err("Value mismatch on $name [$num], expected $#exp_vals values, got $#vals");
125	    return;
126	}
127	$i = 1;
128	$val = shift(@vals);
129	$exp_val = shift(@exp_vals);
130	while (defined $val) {
131	    if (hex($val) != hex($exp_val)) {
132		main::reg_err("Value mismatch on $name [$num] item $i, expected $exp_val, got $val");
133		return;
134	    }
135	    $val = shift(@vals);
136	    $exp_val = shift(@exp_vals);
137	    $i++;
138	}
139    } else {
140	main::reg_err("Invalid type on $name [$num]: $exp_type");
141	return;
142    }
143}
144
145sub check_fru_mr_data {
146    my $fru = shift;
147    my $num = shift;
148    my $exp_type = shift;
149    my $exp_version = shift;
150    my $exp_val = shift;
151    my ($type, $version, $val);
152    my $readval = $fru->get_multirecord($num);
153
154    if (!defined $readval) {
155	if (defined $exp_type) {
156	    main::reg_err("value for multirecord [$num] was undefined, expected value");
157	}
158	return;
159    }
160
161    ($type, $version, $val) = split /\s+/, $readval, 3;
162
163    if ($type ne $exp_type) {
164	    main::reg_err("Value mismatch on multirecord [$num] type, expected $exp_type, got $type");
165	    return;
166    }
167
168    if ($version ne $exp_version) {
169	    main::reg_err("Value mismatch on multirecord [$num] version, expected $exp_version, got $version");
170	    return;
171    }
172
173    # If the length of the thing is zero, there won't be anything in the string.  That's ok,
174    # but make the comparisons work.
175    if (!defined $val) {
176	$val = "";
177    }
178
179    my @vals = split /\s+/, $val;
180    my @exp_vals = split /\s+/, $exp_val;
181    my $i;
182    if ($#vals != $#exp_vals) {
183	main::reg_err("Value mismatch on multirecord [$num], expected $#exp_vals values, got $#vals");
184	return;
185    }
186    $i = 1;
187    $val = shift(@vals);
188    $exp_val = shift(@exp_vals);
189    while (defined $val) {
190	if (hex($val) != hex($exp_val)) {
191	    main::reg_err("Value mismatch on multirecord [$num], expected $exp_val, got $val");
192	    return;
193	}
194	$val = shift(@vals);
195	$exp_val = shift(@exp_vals);
196	$i++;
197    }
198}
199
200sub fru_1_data_check {
201    my $fru = shift;
202    my $version = shift;
203
204    main::check_fru_data($fru, "internal_use_version", -1, "integer", "1");
205    if ($version == 0) {
206	main::check_fru_data($fru, "internal_use", -1, "binary",
207			     "0x02 0x03 0x04 0x05 0x06 0x07 0x08");
208    } else {
209	main::check_fru_data($fru, "internal_use", -1, "binary",
210			     "0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10");
211    }
212
213    main::check_fru_data($fru, "chassis_info_version", -1, "integer", "1");
214    main::check_fru_data($fru, "chassis_info_type", -1, "integer", "1");
215    if ($version == 0) {
216	main::check_fru_data($fru, "chassis_info_part_number", -1, "ascii",
217			     "ATCA");
218	main::check_fru_data($fru, "chassis_info_serial_number", -1, "ascii",
219			     "Tes0");
220	  main::check_fru_data($fru, "chassis_info_custom", 0, "ascii", "");
221	  main::check_fru_data($fru, "chassis_info_custom", 1, "ascii", "xyz");
222	  main::check_fru_data($fru, "chassis_info_custom", 2, "ascii", undef);
223    } else {
224	main::check_fru_data($fru, "chassis_info_part_number", -1, "binary",
225			     "0x40");
226	main::check_fru_data($fru, "chassis_info_serial_number", -1, "binary",
227			     "0x99 0x88 0x77");
228	main::check_fru_data($fru, "chassis_info_custom", 0, "ascii", undef);
229    }
230
231    main::check_fru_data($fru, "board_info_version", -1, "integer", "1");
232    # OpenIPMI converts 0 to 25 (english)
233    main::check_fru_data($fru, "board_info_lang_code", -1, "integer", "25");
234    main::check_fru_data($fru, "board_info_mfg_time", -1, "time", "820476000");
235    main::check_fru_data($fru, "board_info_board_manufacturer", -1, "ascii", "Tes1");
236    if ($version == 0) {
237    	main::check_fru_data($fru, "board_info_board_product_name", -1, "ascii", "am4001");
238    } else {
239    	main::check_fru_data($fru, "board_info_board_product_name", -1, "ascii", "AM4001");
240    }
241    main::check_fru_data($fru, "board_info_board_serial_number", -1, "ascii", "Tes3");
242    main::check_fru_data($fru, "board_info_board_part_number", -1, "ascii", "Tes4");
243    main::check_fru_data($fru, "board_info_fru_file_id", -1, "ascii", "Tes5");
244    main::check_fru_data($fru, "board_info_custom", 0, "ascii", undef);
245
246    main::check_fru_data($fru, "product_info_version", -1, "integer", "1");
247    # OpenIPMI converts 0 to 25 (english)
248    main::check_fru_data($fru, "product_info_lang_code", -1, "integer", "25");
249    main::check_fru_data($fru, "product_info_manufacturer_name", -1, "ascii", "");
250    if ($version == 0) {
251	main::check_fru_data($fru, "product_info_product_name", -1, "ascii", "Te6");
252    } else {
253	main::check_fru_data($fru, "product_info_product_name", -1, "ascii", "TE");
254    }
255    main::check_fru_data($fru, "product_info_product_part_model_number", -1, "ascii",
256			 "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz01234");
257    main::check_fru_data($fru, "product_info_product_version", -1, "binary",
258			 "0x01 0x02 0x03 0x04 0x05 0x06 0x07");
259    main::check_fru_data($fru, "product_info_product_serial_number", -1, "ascii", "");
260    main::check_fru_data($fru, "product_info_asset_tag", -1, "ascii", "12345678");
261    main::check_fru_data($fru, "product_info_fru_file_id", -1, "ascii", '3=8FH$ B');
262    main::check_fru_data($fru, "product_info_custom", 0, "ascii", 'abcd');
263    if ($version == 0) {
264	main::check_fru_data($fru, "product_info_custom", 1, "ascii", undef);
265    } else {
266	main::check_fru_data($fru, "product_info_custom", 1, "ascii",
267			     "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz01234");
268	main::check_fru_data($fru, "product_info_custom", 2, "ascii",
269			     "ASDF1234*()");
270	main::check_fru_data($fru, "product_info_custom", 3, "ascii",
271			     "1234567890--.");
272    }
273
274
275    if ($version == 0) {
276	main::check_fru_mr_data($fru, 0, 0xc0, 2, "0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10");
277	main::check_fru_mr_data($fru, 1, 0xc1, 2, "");
278	main::check_fru_mr_data($fru, 2, undef, undef, undef);
279    } else {
280	main::check_fru_mr_data($fru, 0, 0xc1, 2, "");
281	main::check_fru_mr_data($fru, 1, 0xc2, 2, "0x87 0x55 0x23 0x32 0x99");
282	main::check_fru_mr_data($fru, 2, 0xc3, 2, "0x87 0xfe 0x99");
283	main::check_fru_mr_data($fru, 3, 0xc4, 2, "");
284	main::check_fru_mr_data($fru, 4, undef, undef, undef);
285    }
286}
287
288sub check_area_offset {
289    my $fru = shift;
290    my $area = shift;
291    my $exp_size = shift;
292    my $size;
293    my $rv;
294
295    $rv = $fru->area_get_offset($area, \$size);
296    if ($rv) {
297	if (!defined $exp_size) {
298	    # expected
299	    return;
300	}
301
302	main::reg_err("or getting area offset for area $area: $rv");
303	return;
304    }
305
306    if ($size != $exp_size) {
307	main::reg_err(" offset for area $area was $size, expected $exp_size");
308    }
309}
310
311sub check_area_length {
312    my $fru = shift;
313    my $area = shift;
314    my $exp_length = shift;
315    my $length;
316    my $rv;
317
318    $rv = $fru->area_get_length($area, \$length);
319    if ($rv) {
320	if (!defined $exp_length) {
321	    # expected
322	    return;
323	}
324
325	main::reg_err("Error getting area length for area $area: $rv");
326	return;
327    }
328
329    if ($length != $exp_length) {
330	main::reg_err("FRU length for area $area was $length, expected $exp_length");
331    }
332}
333
334sub check_area_used_length {
335    my $fru = shift;
336    my $area = shift;
337    my $exp_length = shift;
338    my $length;
339    my $rv;
340
341    $rv = $fru->area_get_used_length($area, \$length);
342    if ($rv) {
343	if (!defined $exp_length) {
344	    # expected
345	    return;
346	}
347
348	main::reg_err("Error getting area used length for area $area: $rv");
349	return;
350    }
351
352    if ($length != $exp_length) {
353	main::reg_err("FRU used length for area $area was $length, expected $exp_length");
354    }
355}
356
357$tmpfru = undef;
358
359sub check_area {
360    my $fru = shift;
361    my $area = shift;
362    my $exp_offset = shift;
363    my $exp_length = shift;
364    my $exp_used_length = shift;
365
366    check_area_offset($fru, $area, $exp_offset);
367    check_area_length($fru, $area, $exp_length);
368    check_area_used_length($fru, $area, $exp_used_length);
369}
370
371sub dup_fru {
372    my $newfru = shift;
373    my $oldfru = shift;
374    my $i;
375    my $rv;
376    my $str;
377    my $num;
378    my $oldnum;
379
380    for $i (0 .. 4) {
381	my ($offset, $length);
382	$rv = $oldfru->area_get_offset($i, \$offset);
383	if ($rv) {
384	    main::reg_err("Error getting old FRU offset: $rv");
385	    $h->close();
386	    return $rv;
387	}
388	$rv = $oldfru->area_get_length($i, \$length);
389	if ($rv) {
390	    main::reg_err("Error getting old FRU offset: $rv");
391	    $h->close();
392	    return $rv;
393	}
394	$rv = $newfru->add_area($i, $offset, $length);
395	if ($rv) {
396	    main::reg_err("Error adding FRU area: $rv");
397	    $h->close();
398	    return $rv;
399	}
400    }
401
402    $i = 0;
403    $num = 0;
404    $oldnum = $num;
405    $str = $oldfru->get($i, \$num);
406    while (defined $str) {
407	my ($name, $type, $val) = split /\s+/, $str, 3;
408
409	# Make sure to ignore the FRU area things when setting values.
410	if (!($name =~ /.*_length/) && !($name =~ /.*_offset/) && defined $type) {
411	    $rv = $newfru->set($i, $oldnum, $type, $val);
412
413	    # Check if we didn't have write permissions, that's ok just go on.
414	    if ($rv && ($rv != $OpenIPMI::eperm)) {
415		main::reg_err("Error setting fru field $i [$num]: $rv");
416		  $h->close();
417		  return $rv;
418	      }
419	}
420	if ($num < 1) {
421	    $num = 0;
422	    $i++;
423	}
424	$oldnum = $num;
425	$str = $oldfru->get($i, \$num);
426    }
427
428    $num = $oldfru->get_num_multi_records() - 1;
429    for $i (0 .. $num) {
430	my ($type, $ver, $val) = split /\s+/, $oldfru->get_multirecord($i), 3;
431
432	# If the data is empty for the record, val will be undefined by
433	# the split, but we need to define it because undefined means
434	# delete.
435	if (!defined $val) {
436	    $val = "";
437	}
438
439	$rv = $newfru->set_multirecord($i, $type, $ver, $val);
440	if ($rv) {
441	    main::reg_err("Error setting multi-record [$i]: $rv");
442	    $h->close();
443	    return $rv;
444	}
445    }
446}
447
448{
449    package CloseDomain;
450    sub new {
451	my $a = shift;
452	my $b = \$a;
453	$b = bless $b;
454	return $b;
455    }
456
457    sub domain_cb {
458	my $self = shift;
459	my $domain = shift;
460
461	$domain->close($$self);
462    }
463
464    package CheckRead4;
465
466    sub new {
467	my $self = shift;
468	my $a = {};
469	$a->{handler} = shift;
470	return bless \$a;
471    }
472
473    sub fru_fetched {
474	print "CheckRead4: Fru fetched\n";
475
476	my $self = shift;
477	my $domain = shift;
478	my $fru = shift;
479	my $err = shift;
480	my $h = $$self->{handler};
481	my $rv;
482	my $i;
483	my $num;
484	my $str;
485
486	if ($err) {
487	    main::reg_err("Error reading the second FRU: $err");
488	    $h->close();
489	    return;
490	}
491
492	print "Second FRU fetched\n";
493	main::fru_1_data_check($fru, 1);
494
495	# Done with tests
496	$h->close();
497    }
498
499    package CheckRead3;
500
501    sub new {
502	my $self = shift;
503	my $a = {};
504	$a->{handler} = shift;
505	return bless \$a;
506    }
507
508    sub fru_written {
509	print "CheckRead3: Fru written\n";
510
511	my $self = shift;
512	my $domain = shift;
513	my $fru = shift;
514	my $err = shift;
515	my $h = $$self->{handler};
516	my $rv;
517
518	if ($err) {
519	    main::reg_err("Error writing the FRU: $err");
520	    $h->close();
521	    return;
522	}
523
524	print "FRU written\n";
525
526	# Now re-read the FRU.
527	$rv = $domain->fru_alloc(1, 0x20, 1, 0, 0, 0, CheckRead4->new($h));
528	if (! defined $rv) {
529	    main::reg_err("Error starting fru fetch: $rv");
530	    $self->close();
531	    return;
532	}
533    }
534
535    sub fru_fetched {
536	print "CheckRead3: Fru fetched\n";
537
538	my $self = shift;
539	my $domain = shift;
540	my $fru = shift;
541	my $err = shift;
542	my $h = $$self->{handler};
543	my $rv;
544	my $i;
545	my $num;
546	my $str;
547
548	if ($err != $OpenIPMI::enosys) {
549	    main::reg_err("Wrong error reading the FRU: $err");
550	    $h->close();
551	    return;
552	}
553
554	print "Bad FRU fetched, copy the prevous FRU to it\n";
555	main::dup_fru($fru, $main::tmpfru);
556	if (main::get_errcount()) {
557	    $h->close();
558	    return;
559	}
560	main::fru_1_data_check($fru, 1);
561	if (main::get_errcount()) {
562	    $h->close();
563	    return;
564	}
565
566	$rv = $fru->write($self);
567	if ($rv) {
568	    main::reg_err("Error writing FRU: $rv");
569	    $h->close();
570	}
571    }
572
573    package CheckRead2;
574
575    sub new {
576	my $self = shift;
577	my $a = {};
578	$a->{handler} = shift;
579	return bless \$a;
580    }
581
582    sub fru_fetched {
583	print "CheckRead2: Fru fetched\n";
584
585	my $self = shift;
586	my $domain = shift;
587	my $fru = shift;
588	my $err = shift;
589	my $h = $$self->{handler};
590	my $rv;
591	my $i;
592	my $num;
593
594	if ($err) {
595	    main::reg_err("Error reading the FRU: $err");
596	    $main::lanserv->clearlines();
597	    $main::lanserv->cmd("mc_dump_fru_data 20 0");
598	    for ($i=0; $i<128; $i++) {
599		print $main::lanserv->waitnextline(), "\n";
600	    }
601	    $h->close();
602	    return;
603	}
604
605	print "FRU refetched\n";
606	main::fru_1_data_check($fru, 1);
607	main::check_area($fru, 0, 8, 16, 16);
608	main::check_area($fru, 1, 24, 16, 11);
609	main::check_area($fru, 2, 96, 40, 34);
610	main::check_area($fru, 3, 192, 256, 181);
611	main::check_area($fru, 4, 512, 512, 28);
612
613	$main::tmpfru = $fru;
614
615	print "Fetch a bad FRU\n";
616	$rv = $domain->fru_alloc(1, 0x20, 1, 0, 0, 0, CheckRead3->new($h));
617	if (! defined $rv) {
618	    main::reg_err("Error starting fru fetch: $rv");
619	    $h->close();
620	    return;
621	}
622    }
623
624    package CheckRead1;
625
626    sub new {
627	my $self = shift;
628	my $a = {};
629	$a->{handler} = shift;
630	return bless \$a;
631    }
632
633    sub fru_written {
634	print "CheckRead1: Fru written\n";
635
636	my $self = shift;
637	my $domain = shift;
638	my $fru = shift;
639	my $err = shift;
640	my $h = $$self->{handler};
641	my $rv;
642
643	if ($err) {
644	    main::reg_err("Error writing the FRU: $err");
645	    $h->close();
646	    return;
647	}
648
649	print "FRU written\n";
650
651	# Now re-read the FRU.
652	$rv = $domain->fru_alloc(1, 0x20, 0, 0, 0, 0, CheckRead2->new($h));
653	if (! defined $rv) {
654	    main::reg_err("Error starting fru fetch: $rv");
655	    $self->close();
656	    return;
657	}
658    }
659
660    sub fru_fetched {
661	print "CheckRead1: Fru fetched\n";
662
663	my $self = shift;
664	my $domain = shift;
665	my $fru = shift;
666	my $err = shift;
667	my $h = $$self->{handler};
668	my $rv;
669	my $idx;
670	my $num;
671
672	if ($err) {
673	    main::reg_err("Error reading the FRU: $err");
674	    $h->close();
675	    return;
676	}
677
678	print "FRU data read, checking\n";
679	main::check_area($fru, 0, 8, 8, 8);
680	main::check_area($fru, 1, 16, 24, 20);
681	main::check_area($fru, 2, 48, 40, 35);
682	main::check_area($fru, 3, 88, 104, 100);
683	main::check_area($fru, 4, 192, 832, 26);
684
685	main::fru_1_data_check($fru, 0);
686
687	if (main::get_errcount()) {
688	    $h->close();
689	    return;
690	}
691
692	print "Fiddle with area offsets and lengths\n";
693	# Move the chassis info area out by 16.  This should fail.
694	$rv = $fru->area_set_offset(1, 32);
695	if (!$rv) {
696	    main::reg_err("Setting the FRU offset to a used offset didn't fail");
697	    $h->close();
698	    return;
699	}
700
701	# Move the chassis info area out by 8.  This should succeed.
702	$rv = $fru->area_set_offset(1, 24);
703	if ($rv) {
704	    main::reg_err("Error setting the FRU offset: $rv");
705	    $h->close();
706	    return;
707	}
708
709	# Increasing the length should fail.
710	$rv = $fru->area_set_length(1, 32);
711	if (!$rv) {
712	    main::reg_err("Increasing the FRU length didn't fail");
713	    $h->close();
714	    return;
715	}
716
717	# Decreasing the length should fail.
718	$rv = $fru->area_set_length(1, 16);
719	if (!$rv) {
720	    main::reg_err("Decreasing the FRU length didn't fail");
721	    $h->close();
722	    return;
723	}
724
725	# This should just barely fit.
726	$rv = $fru->set($fru_field_table->{chassis_info_part_number},
727			-1, "ascii", "abcdefg");
728	if ($rv) {
729	    main::reg_err("Error setting chassis info part number to 8: $rv");
730	    $h->close();
731	    return;
732	}
733	main::check_fru_data($fru, "chassis_info_part_number", -1, "ascii",
734			     "abcdefg");
735
736	# This should not fit.
737	$rv = $fru->set($fru_field_table->{chassis_info_part_number},
738			-1, "ascii", "jjjjjjjjj");
739	if (!$rv) {
740	    main::reg_err("Setting value of chassis info long didn't fail");
741	    $h->close();
742	    return;
743	}
744	main::check_fru_data($fru, "chassis_info_part_number", -1, "ascii",
745			     "abcdefg");
746
747	# Decrease the size of the chassis info area so we are 1 byte
748	# too long to decrease the length.
749	$rv = $fru->set_array($fru_field_table->{chassis_info_part_number},
750			      -1, "binary", [ 0x40 ]);
751	if ($rv) {
752	    main::reg_err("Error setting chassis info part number: $rv");
753	    $h->close();
754	    return;
755	}
756	main::check_fru_data($fru, "chassis_info_part_number", -1, "binary",
757			     "0x40");
758
759	# Decreasing the length should fail.
760	$rv = $fru->area_set_length(1, 16);
761	if (!$rv) {
762	    main::reg_err("Decreasing the FRU length didn't fail");
763	    $h->close();
764	    return;
765	}
766
767	main::check_area($fru, 0, 8, 8, 8);
768	main::check_area($fru, 1, 24, 24, 17);
769	main::check_area($fru, 2, 48, 40, 35);
770	main::check_area($fru, 3, 88, 104, 100);
771	main::check_area($fru, 4, 192, 832, 26);
772
773	print "Fiddle with area size\n";
774	# Decrease the size of the chassis info area so we can decrease
775	# the length.
776	$rv = $fru->set($fru_field_table->{chassis_info_serial_number},
777			-1, "binary", "0x99 0x88 0x77");
778	if ($rv) {
779	    main::reg_err("Error setting chassis info serial number: $rv");
780	    $h->close();
781	    return;
782	}
783
784	# Decreasing the length should succeed.
785	$rv = $fru->area_set_length(1, 16);
786	if ($rv) {
787	    main::reg_err("Error decreasing chassis info area length: $rv");
788	    $h->close();
789	    return;
790	}
791
792	main::check_area($fru, 0, 8, 8, 8);
793	main::check_area($fru, 1, 24, 16, 16);
794	main::check_area($fru, 2, 48, 40, 35);
795	main::check_area($fru, 3, 88, 104, 100);
796	main::check_area($fru, 4, 192, 832, 26);
797
798	print "Fiddle with area size some more\n";
799	# Decreasing the length should succeed.
800	$rv = $fru->area_set_length(1, 16);
801	if ($rv) {
802	    main::reg_err("Error decreasing chassis info area length: $rv");
803	    $h->close();
804	    return;
805	}
806
807	print "Move areas around\n";
808	# Move everything out.
809	$rv = $fru->area_set_offset(4, 512);
810	if ($rv) {
811	    main::reg_err("Error setting multi-record offset: $rv");
812	    $h->close();
813	    return;
814	}
815	$rv = $fru->area_set_offset(3, 192);
816	if ($rv) {
817	    main::reg_err("Error setting product info offset: $rv");
818	    $h->close();
819	    return;
820	}
821	$rv = $fru->area_set_offset(2, 96);
822	if ($rv) {
823	    main::reg_err("Error setting board info offset: $rv");
824	    $h->close();
825	    return;
826	}
827
828	main::check_area($fru, 0, 8, 8, 8);
829	main::check_area($fru, 1, 24, 16, 16);
830	main::check_area($fru, 2, 96, 40, 35);
831	main::check_area($fru, 3, 192, 104, 100);
832	main::check_area($fru, 4, 512, 512, 26);
833
834	print "Increasing length of product info area and adding values\n";
835	$rv = $fru->area_set_length(3, 256);
836	if ($rv) {
837	    main::reg_err("Error increasing product info area length: $rv");
838	    $h->close();
839	    return;
840	}
841	main::check_area($fru, 0, 8, 8, 8);
842	main::check_area($fru, 1, 24, 16, 16);
843	main::check_area($fru, 2, 96, 40, 35);
844	main::check_area($fru, 3, 192, 256, 100);
845	main::check_area($fru, 4, 512, 512, 26);
846
847	# This should truncate
848	$rv = $fru->set($fru_field_table->{product_info_custom}, 7, "ascii",
849			"abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz012345");
850	if ($rv) {
851	    main::reg_err("Error setting product info custom 1: $rv");
852	    $h->close();
853	    return;
854	}
855	main::check_fru_data($fru, "product_info_custom", 1, "ascii",
856			     "abcdefghijklmnopqrstuvwxyz012345abcdefghijklmnopqrstuvwxyz01234");
857	main::check_area($fru, 3, 192, 256, 164);
858	$rv = $fru->set($fru_field_table->{product_info_custom}, 7, "ascii",
859			"ASDF1234*()");
860	if ($rv) {
861	    main::reg_err("Error setting product info custom 2: $rv");
862	    $h->close();
863	    return;
864	}
865	main::check_fru_data($fru, "product_info_custom", 2, "ascii",
866			     "ASDF1234*()");
867	main::check_area($fru, 3, 192, 256, 174);
868
869	# Convert an 8-bit field to a 6-bit field in board info
870	$rv = $fru->set($fru_field_table->{board_info_board_product_name}, -1,
871			"ascii", "AM4001");
872	if ($rv) {
873	    main::reg_err("Error setting board info board product name: $rv");
874	    $h->close();
875	    return;
876	}
877
878	# Convert an 8-bit field to a 6-bit field
879	$rv = $fru->set($fru_field_table->{product_info_product_name}, -1, "ascii",
880			"TE");
881	if ($rv) {
882	    main::reg_err("Error setting product info product name: $rv");
883	    $h->close();
884	    return;
885	}
886	main::check_fru_data($fru, "product_info_product_name", -1, "ascii",
887			     "TE");
888
889	$rv = $fru->set($fru_field_table->{product_info_custom}, 7, "ascii",
890			"1234567890--.");
891	if ($rv) {
892	    main::reg_err("Error setting product info custom 3: $rv");
893	    $h->close();
894	    return;
895	}
896	main::check_fru_data($fru, "product_info_custom", 3, "ascii",
897			     "1234567890--.");
898	$rv = $fru->set_multirecord(7, 0xc2, 2, "0x87 0x55 0x23 0x32 0x99");
899	if ($rv) {
900	    main::reg_err("Error setting multirecord 2: $rv");
901	    $h->close();
902	    return;
903	}
904	main::check_fru_mr_data($fru, 2, 0xc2, 2, "0x87 0x55 0x23 0x32 0x99");
905	main::check_area($fru, 4, 512, 512, 36);
906	$rv = $fru->set_multirecord_array(7, 0xc3, 2, [ 0x87, 0xfe, 0x99 ]);
907	if ($rv) {
908	    main::reg_err("Error setting multirecord 3: $rv");
909	    $h->close();
910	    return;
911	}
912	main::check_fru_mr_data($fru, 3, 0xc3, 2, "0x87 0xfe 0x99");
913	main::check_area($fru, 4, 512, 512, 44);
914	$rv = $fru->set_multirecord_array(7, 0xc4, 2, [ ]);
915	if ($rv) {
916	    main::reg_err("Error setting multirecord 3: $rv");
917	    $h->close();
918	    return;
919	}
920	main::check_fru_mr_data($fru, 4, 0xc4, 2, "");
921	main::check_area($fru, 4, 512, 512, 49);
922	$rv = $fru->set_multirecord(7, 0xc5, 2, "");
923	if ($rv) {
924	    main::reg_err("Error setting multirecord 2: $rv");
925	    $h->close();
926	    return;
927	}
928	main::check_fru_mr_data($fru, 5, 0xc5, 2, "");
929	main::check_area($fru, 4, 512, 512, 54);
930
931	$rv = $fru->set_multirecord(0, 0, 0, undef);
932	if ($rv) {
933	    main::reg_err("Error deleting multirecord 0: $rv");
934	    $h->close();
935	    return;
936	}
937	main::check_area($fru, 4, 512, 512, 33);
938
939	$rv = $fru->set_multirecord(4, 0, 0, undef);
940	if ($rv) {
941	    main::reg_err("Error deleting multirecord 4: $rv");
942	    $h->close();
943	    return;
944	}
945	main::check_area($fru, 4, 512, 512, 28);
946
947	$rv = $fru->set($fru_field_table->{chassis_info_custom}, 1, "ascii",
948			undef);
949	if ($rv) {
950	    main::reg_err("Error clearing chassis info custom 1: $rv");
951	    $h->close();
952	    return;
953	}
954	main::check_fru_data($fru, "chassis_info_custom", 1, "ascii", undef);
955	main::check_area($fru, 1, 24, 16, 12);
956	$rv = $fru->set($fru_field_table->{chassis_info_custom}, 0, "ascii",
957			undef);
958	if ($rv) {
959	    main::reg_err("Error clearing chassis info custom 0: $rv");
960	    $h->close();
961	    return;
962	}
963	main::check_fru_data($fru, "chassis_info_custom", 0, "ascii", undef);
964	main::check_area($fru, 1, 24, 16, 11);
965
966	print "Cleaning up the internal area\n";
967	$rv = $fru->area_set_length(0, 16);
968	if ($rv) {
969	    main::reg_err("Error increasing internal use area length: $rv");
970	    $h->close();
971	    return;
972	}
973	$rv = $fru->set($fru_field_table->{internal_use}, 0, "binary",
974			"0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10");
975	if ($rv) {
976	    main::reg_err("Error clearing chassis info custom 0: $rv");
977	    $h->close();
978	    return;
979	}
980
981	print "Writing out the FRU\n";
982	# Make sure it's what we want.
983	main::fru_1_data_check($fru, 1);
984
985	$rv = $fru->write($self);
986	if ($rv) {
987	    main::reg_err("Error writing FRU: $rv");
988	    $h->close();
989	}
990    }
991
992
993    package Handlers;
994
995    sub new {
996	my $a = {};
997	$a->{keepon} = 1;
998	return bless \$a;
999    }
1000
1001    sub log {
1002	my $self = shift;
1003	my $level = shift;
1004	my $log = shift;
1005
1006	print $level, ": ", $log, "\n";
1007    }
1008
1009    sub conn_change_cb {
1010	my $self = shift;
1011	my $domain = shift;
1012	my $err = shift;
1013	my $conn_num = shift;
1014	my $port_num = shift;
1015	my $still_connected = shift;
1016	my $rv;
1017
1018	if ($err) {
1019	    main::reg_err("Error starting up IPMI connection: $err");
1020	    $self->close();
1021	    return;
1022	}
1023
1024	print "Connection up!\n";
1025	$rv = $domain->fru_alloc(1, 0x20, 0, 0, 0, 0, CheckRead1->new($self));
1026	if (! defined $rv) {
1027	    main::reg_err("Error starting fru fetch: $rv");
1028	    $self->close();
1029	    return;
1030	}
1031    }
1032
1033    sub domain_close_done_cb {
1034	my $self = shift;
1035
1036	$$self->{keepon} = 0;
1037    }
1038
1039    sub close {
1040	my $self = shift;
1041	my $domain = shift;
1042
1043	if (defined $$self->{domain_id}) {
1044	    my $v = CloseDomain::new($self);
1045	    $$self->{domain_id}->to_domain($v);
1046	} else {
1047	    $$self->{keepon} = 0;
1048	}
1049    }
1050
1051}
1052
1053package main;
1054
1055$lanserv = Lanserv->new();
1056if (! $lanserv) {
1057    main::reg_err("Unable to start lanserv");
1058    exit(1);
1059}
1060
1061# Add a BMC
1062$lanserv->cmd("mc_add 0x20 0 has-device-sdrs 0x23 9 8 0x1f 0x1291 0xf02");
1063$lanserv->cmd("main_sdr_add 0x20 0x00 0x00 0x51 0x12 0x0f 0x20 0x00 0x00 0x1f 0x00 0x00 0x00 0x07 0x01 0x00 0xc4 'T 'e 's 't");
1064
1065# Create some FRU information
1066$lanserv->cmd
1067    ("mc_add_fru_data 0x20 0x0 0x400 data " .
1068     " 0x01 0x01 0x02 0x06 0x0b 0x18 0x00 0xd3" .
1069# Internal Use
1070     " 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08" .
1071# Chassis info
1072     " 0x01 0x03 0x01 0xc4 'A 'T 'C 'A" .
1073     " 0xc4 'T 'e 's '0 0xc0 0xc3 'x" .
1074     " 'y 'z 0xc1 0x00 0x00 0x00 0x00 0x4f" .
1075# Expansion space
1076     " 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00" .
1077# Board info
1078     " 0x01 0x05 0x00 0x00 0x00 0x00 0xc4 'T" .
1079     " 'e 's '1 0xc6 'a 'm '4 '0" .
1080     " '0 '1 0xc4 'T 'e 's '3 0xc4" .
1081     " 'T 'e 's '4 0xc4 'T 'e 's" .
1082     " '5 0xc1 0x00 0x00 0x00 0x00 0x00 0x53" .
1083#Product Info
1084     " 0x01 0x0d 0x00 0xc0 0xc3 'T 'e '6" .
1085     " 0xff 'a 'b 'c 'd 'e 'f 'g" .
1086     " 'h 'i 'j 'k 'l 'm 'n 'o" .
1087     " 'p 'q 'r 's 't 'u 'v 'w" .
1088     " 'x 'y 'z '0 '1 '2 '3 '4" .
1089     " '5 'a 'b 'c 'd 'e 'f 'g" .
1090     " 'h 'i 'j 'k 'l 'm 'n 'o" .
1091     " 'p 'q 'r 's 't 'u 'v 'w" .
1092     " 'x 'y 'z '0 '1 '2 '3 '4" .
1093     " 0x07 0x01 0x02 0x03 0x04 0x05 0x06 0x07" .
1094     " 0xc0 0x48 0x21 0x43 0x65 0x87 0x88 0x53" .
1095     " 0x87 0x99 0x28 0x01 0x88 0xc4 'a 'b" .
1096     " 'c 'd 0xc1 0x00 0x00 0x00 0x00 0xe4" .
1097# Multi-records
1098     " 0xc0 0x02 0x10 0x78 0xb6 0x01 0x02 0x03" .
1099     " 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b" .
1100     " 0x0c 0x0d 0x0e 0x0f 0x10 0xc1 0x82 0x00" .
1101     " 0x00 0xbd"
1102);
1103
1104# Create a dummy FRU that has an error
1105$lanserv->cmd("mc_add_fru_data 0x20 1 0x400 data 0x01");
1106
1107$lanserv->cmd("mc_enable 0x20");
1108
1109sleep 1;
1110
1111#OpenIPMI::enable_debug_msg();
1112OpenIPMI::enable_debug_malloc();
1113
1114# Now start OpenIPMI
1115$rv = OpenIPMI::init();
1116if ($rv != 0) {
1117    print "init failed";
1118    exit 1;
1119}
1120
1121$i = 0;
1122$s = OpenIPMI::fru_index_to_str($i);
1123while (defined $s) {
1124    $fru_field_table->{$s} = $i;
1125    $i++;
1126    $s = OpenIPMI::fru_index_to_str($i);
1127}
1128
1129$h = Handlers::new();
1130
1131OpenIPMI::set_log_handler($h);
1132
1133@args = ( "-noseteventrcvr",
1134	  "lan", "-p", "9000", "-U", "minyard", "-P", "test", "localhost");
1135$$h->{domain_id} = OpenIPMI::open_domain2("test", \@args, $h, \undef);
1136if (! $$h->{domain_id}) {
1137    $lanserv->close();
1138    print "IPMI open failed\n";
1139    exit 1;
1140}
1141
1142while ($$h->{keepon}) {
1143    OpenIPMI::wait_io(1000);
1144}
1145
1146# Make sure our copy of the FRU is destroyed.
1147$main::tmpfru = undef;
1148
1149$lanserv->close();
1150OpenIPMI::shutdown_everything();
1151exit main::get_errcount();
1152