diff options
author | Pascal Rigaux <pixel@mandriva.com> | 2005-02-23 17:33:43 +0000 |
---|---|---|
committer | Pascal Rigaux <pixel@mandriva.com> | 2005-02-23 17:33:43 +0000 |
commit | c491f8e99349e4022fac0dec6536ab4b43396507 (patch) | |
tree | dfc2f6bba4d40995ef4e0c152a533ad12ff47d1e /monitor-parse-edid | |
parent | 3b6ec4ddecee5811e38d757f15a56edfbce79b4a (diff) | |
download | monitor-edid-c491f8e99349e4022fac0dec6536ab4b43396507.tar monitor-edid-c491f8e99349e4022fac0dec6536ab4b43396507.tar.gz monitor-edid-c491f8e99349e4022fac0dec6536ab4b43396507.tar.bz2 monitor-edid-c491f8e99349e4022fac0dec6536ab4b43396507.tar.xz monitor-edid-c491f8e99349e4022fac0dec6536ab4b43396507.zip |
*** empty log message ***
Diffstat (limited to 'monitor-parse-edid')
-rwxr-xr-x | monitor-parse-edid | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/monitor-parse-edid b/monitor-parse-edid new file mode 100755 index 0000000..b4b377c --- /dev/null +++ b/monitor-parse-edid @@ -0,0 +1,279 @@ +#!/usr/bin/perl + +my @edid_info = group_by2( + a8 => '_header', + a2 => 'manufacturer_name', + + v => 'product_code', + V => 'serial_number', + C => 'week', + C => 'year', + C => 'edid_version', + C => 'edid_revision', + a => 'video_input_definition', + + C => 'max_size_horizontal', # in cm + C => 'max_size_vertical', # in cm + C => 'gamma', + a => 'feature_support', + a10 => '_color_characteristics', + a3 => '_established_timings', + a16 => '_standard_timings', + a72 => 'monitor_details', + + C => 'extension_flag', + C => 'checksum', +); + +my %subfields = ( + manufacturer_name => [ group_by2( + 1 => '', + 5 => '1', + 5 => '2', + 5 => '3', + ) ], + + video_input_definition => [ group_by2( + 1 => 'digital', + 1 => 'separate_sync', + 1 => 'composite_sync', + 1 => 'sync_on_green', + 2 => '', + 2 => 'voltage_level', + ) ], + + feature_support => [ group_by2( + 1 => 'DPMS_standby', + 1 => 'DPMS_suspend', + 1 => 'DPMS_active_off', + 1 => '', + 1 => 'rgb', + ) ], + + detailed_timing => [ group_by2( + 8 => 'horizontal_active', + 8 => 'horizontal_blanking', + 4 => 'horizontal_active_hi', + 4 => 'horizontal_blanking_hi', + 8 => 'vertical_active', + 8 => 'vertical_blanking', + 4 => 'vertical_active_hi', + 4 => 'vertical_blanking_hi', + 8 => 'horizontal_sync_offset', + 8 => 'horizontal_sync_pulse_width', + 4 => 'vertical_sync_offset', + 4 => 'vertical_sync_pulse_width', + 2 => 'horizontal_sync_offset_hi', + 2 => 'horizontal_sync_pulse_width_hi', + 2 => 'vertical_sync_offset_hi', + 2 => 'vertical_sync_pulse_width_hi', + 8 => 'horizontal_image_size', # in mm + 8 => 'vertical_image_size', # in mm + 4 => 'horizontal_image_size_hi', + 4 => 'vertical_image_size_hi', + 8 => 'horizontal_border', + 8 => 'vertical_border', + + 1 => 'interlaced', + 2 => 'stereo', + 2 => 'digital_composite', + 1 => 'horizontal_sync_positive', + 1 => 'vertical_sync_positive', + 1 => '', + ) ], + + monitor_range => [ group_by2( + 8 => 'vertical_min', + 8 => 'vertical_max', + 8 => 'horizontal_min', + 8 => 'horizontal_max', + 8 => 'pixel_clock_max', + ) ], +); + +sub get_many_bits { + my ($s, $field_name) = @_; + my @bits = split('', unpack('B*', $s)); + my %h; + foreach (@{$subfields{$field_name}}) { + my ($size, $field) = @$_; + my @l = ('0' x (8 - $size), splice(@bits, 0, $size)); + $h{$field} = unpack("C", pack('B*', join('', @l))) if $field && $field !~ /^_/; + } + \%h; +} + +sub check_parsed_edid { + my ($edid) = @_; + + $edid->{manufacturer_name} ne '@@@' or return 'bad manufacturer_name'; + $edid->{product_code} != 0 or return 'bad product_code'; + $edid->{edid_version} != 0xff && $edid->{edid_revision} != 0xff or return 'bad edid_version'; + + if ($edid->{monitor_range}) { + $edid->{monitor_range}{horizontal_min} && + $edid->{monitor_range}{horizontal_min} <= $edid->{monitor_range}{horizontal_max} + or return 'bad HorizSync'; + $edid->{monitor_range}{vertical_min} && + $edid->{monitor_range}{vertical_min} <= $edid->{monitor_range}{vertical_max} + or return 'bad VertRefresh'; + } + + ''; +} + +sub parse_edid { + my ($raw_edid) = @_; + + my %edid; + my @vals = unpack(join('', map { $_->[0] } @edid_info), $raw_edid); + my $i; + foreach (@edid_info) { + my ($field, $v) = ($_->[1], $vals[$i++]); + + if ($field eq 'year') { + $v += 1990; + } elsif ($field eq 'manufacturer_name') { + my $h = get_many_bits($v, 'manufacturer_name'); + $v = join('', map { chr(ord('A') + $h->{$_} - 1) } 1 .. 3); + } elsif ($field eq 'video_input_definition') { + $v = get_many_bits($v, 'video_input_definition'); + } elsif ($field eq 'feature_support') { + $v = get_many_bits($v, 'feature_support'); + } elsif ($field eq 'monitor_details') { + while ($v) { + (my $pixel_clock, my $vv, $v) = unpack("v a16 a*", $v); + + if ($pixel_clock) { + # detailed timing + my $h = get_many_bits($vv, 'detailed_timing'); + $h->{pixel_clock} = $pixel_clock / 100; # to have it in MHz + + my %detailed_timing_field_size = map { $_->[1], $_->[0] } @{$subfields{detailed_timing}}; + foreach my $field (keys %detailed_timing_field_size) { + $field =~ s/_hi$// or next; + my $hi = delete($h->{$field . '_hi'}); + $h->{$field} += $hi << $detailed_timing_field_size{$field}; + } + push @{$edid{detailed_timings}}, $h; + } else { + (my $flag, $vv) = unpack("n x a*", $vv); + + if ($flag == 0xfd) { + # range + $edid{monitor_range} = get_many_bits($vv, 'monitor_range'); + if ($edid{monitor_range}{pixel_clock_max} == 0xff) { + delete $edid{monitor_range}{pixel_clock_max}; + } else { + $edid{monitor_range}{pixel_clock_max} *= 10; #- to have it in MHz + } + } elsif ($flag == 0xfc) { + my $prev = $edid{monitor_name}; + $edid{monitor_name} = ($prev ? "$prev " : '') . unpack('A13', $vv); + } elsif ($flag == 0xfe) { + push @{$edid{monitor_text}}, unpack('A13', $vv); + } elsif ($flag == 0xff) { + push @{$edid{serial_number2}}, unpack('A13', $vv); + } else { + #warn "parse_edid: unknown flag $flag\n"; + } + } + } + } + + $edid{$field} = $v if $field && $field !~ /^_/; + } + + $edid{EISA} = $edid{manufacturer_name} . sprintf('%x', $edid{product_code}); + + \%edid; +} + +sub print_edid { + my ($edid) = @_; + my $diagonal_inches = sqrt(sqr($edid->{max_size_horizontal}) + + sqr($edid->{max_size_vertical})) / 2.54; + + print "Name: $edid->{monitor_name}\n"; + print "EISA ID: $edid->{EISA}\n"; + printf "Screen size: %d cm x %d cm (%3.2f inches)\n", $edid->{max_size_horizontal}, $edid->{max_size_vertical}, $diagonal_inches * 1.08; + + print "Gamma: ", $edid->{gamma} / 100 + 1, "\n"; + printf "%s signal\n", $edid->{video_input_definition}{digital} ? 'Digital' : 'Analog'; + + if ($edid->{monitor_range}) { + printf "Max video bandwidth: %u MHz\n", $edid->{monitor_range}{pixel_clock_max}; + print "\n"; + printf "\tHorizSync %u-%u\n", $edid->{monitor_range}{horizontal_min}, $edid->{monitor_range}{horizontal_max}; + printf "\tVertRefresh %u-%u\n", $edid->{monitor_range}{vertical_min}, $edid->{monitor_range}{vertical_max}; + } + + foreach my $h (@{$edid->{detailed_timings}}) { + my $horizontal_total = $h->{horizontal_active} + $h->{horizontal_blanking}; + my $vertical_total = $h->{vertical_active} + $h->{vertical_blanking}; + + print "\n"; + printf qq(\t# %dx%d @ %.1f Hz, %.1f kHz hsync\n), + $h->{horizontal_active}, $h->{vertical_active}, + $h->{pixel_clock} / $horizontal_total / $vertical_total * 1000 * 1000, + $h->{pixel_clock} / $horizontal_total * 1000; + + printf qq(\tModeLine "%dx%d" $h->{pixel_clock} %d %d %d %d %d %d %d %d\n), + $h->{horizontal_active}, $h->{vertical_active}, + + $h->{horizontal_active}, + $h->{horizontal_active} + $h->{horizontal_sync_offset}, + $h->{horizontal_active} + $h->{horizontal_sync_offset} + $h->{horizontal_sync_pulse_width}, + $h->{horizontal_total}, + + $h->{vertical_active}, + $h->{vertical_active} + $h->{vertical_sync_offset}, + $h->{vertical_active} + $h->{vertical_sync_offset} + $h->{vertical_sync_pulse_width}, + $h->{vertical_total}; + } +} + +sub usage() { + die "usage: monitor-parse-edid [--perl] [<edid file>]\n"; +} + +use Getopt::Long; +GetOptions( + 'perl' => \ (my $raw_perl), +) or usage(); + +my $F; +if (@ARGV == 0) { + $F = *STDIN; +} elsif (@ARGV == 1) { + open($F, $ARGV[0]) or usage(); +} else { + usage(); +} + +my $raw_edid = join('', <$F>); + +length($raw_edid) == 128 || length($raw_edid) == 256 or die "parse-edid: bad edid length\n"; + +my $edid = parse_edid($raw_edid); +if (my $err = check_parsed_edid($edid)) { + die "$err\n"; +} + +if ($raw_perl) { + use Data::Dumper; + $Data::Dumper::Sortkeys = 1; + print Dumper($edid); +} else { + print_edid($edid); +} + + +sub sqr { $_[0] * $_[0] } +sub group_by2 { + my @l; + for (my $i = 0; $i < @_; $i += 2) { + push @l, [ $_[$i], $_[$i+1] ]; + } + @l; +} |