#!/usr/bin/perl # Alpha version dated: 23rd November 2005. # Analyse a USB log from an LiDE60 scanner. # Copyright (C) 2005 John Dalton # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ################################################ ############### UTILITY FUNCTIONS ############## ################################################ # Convert a (possibly multibyte) BCD number to decimal. # # Inputs: The function takes a variable number of arguments # Each argument is a string of the form "0xhh", representing # a hexadecimal number (note that each hex digit 'h' should be # restricted to the range 0<=h<=9, as we are talkinh BCD here). # The first argument is taken to be the least significant byte # of a multibyte BCD number. # Returns: The value of the BCD number (as an integer). # Example: bcd("0x12", "0x64") returns the integer 6412. # sub bcd { my $result; my $power = 1; while(shift(@_) =~ /0x(\S)(\S)/) { if($1>=0 and $1<=9 and $2>=0 and $2<=9) { $result = $power*($1 * 10 + $2) + $result; } else { $result = "?"; break; } $power = $power * 100; } return $result; } # Convert a (possibly multibyte) hexadecimal number to decimal. # # Inputs: The function takes a variable number of arguments # Each argument is a string of the form "0xhh", representing # a hexadecimal byte. The first argument is taken to be # the least significant byte of a multibyte BCD number. # Returns: The value of the BCD number (as an integer). # Example: decimal("0x12", "0x6a") returns the integer 27154. # sub decimal { my $result; my $byte = shift(@_); my $power = 1; while($byte =~ /0x\S\S/) { $result = $result+$power*hex($byte); $power *= 256; $byte = shift(@_); } return $result; } ################################################ ########## HTML RELATED FUNCTIONS ############## ################################################ # These functions do the 'hard work' of opening and # closing an html file, allowing an html file to be # greated as easily as a plain text file. # Write the opening sequence of an html file. # # Globals: OUTFILE is the file handle of the file to which html # is to be be written. # sub html_open { printf OUTFILE "\n"; printf OUTFILE "\n"; printf OUTFILE "\n"; printf OUTFILE "\n"; printf OUTFILE "\n"; printf OUTFILE "LiDE 60 log: $filename_with_suffix\n"; printf OUTFILE "\n"; printf OUTFILE "\n"; printf OUTFILE "
\n";
}

# Write the closing sequence of an html file.
#
# Globals: OUTFILE is the file handle of the file to which html
#          is to be be written.
#
sub html_close {
    printf OUTFILE "
\n"; printf OUTFILE "\n"; printf OUTFILE "\n"; } ################################################ ####### FUNCTIONS TO MANIPULATE RECORDS ######## ################################################ # The incoming log file is broken into an array # of records. These records can then be manipulated # to form a heirarchy. There should be no need to # access the array of records directly, instead use # the functions provided here. # Add a record to the list of records. # # Globals: # current: The record which has just been created and is to be inserted # at the start of the array of records. # records: The array of records. New records will be inserted at index # '0', so records with larger indices are older. # sub create_record { unshift @records, {%current}; %current = (); #$bottom++; } # Group a set of records together and write them to a file. # Replace the written records with a single new record which is # hyperlinked to the file just created. The root of the tree # will be called "00index.html" and the children will be # called "file_nnnn.html", where nnnn is an integer. # # Inputs: # $num_records : The number of records to be witten to file. # if this argument is zero all remaining records will # be written. # $start_offset = The index of the first record which is to be written. # by default the first record will be at index '$bottom' of # the array of records. # # Globals: # records: The array of records. New records will be inserted at index # '0', so records with larger indices are older. # current: The record which will replace the records written to file. # It should have a field called "buffer", which will be # the text of the hyperlink. If "buffer" has already been # marked up as a hypertest link no new markup will be added. # $bottom : The default index of the first record to be written. It # can be viewed as a pointer to a head of a stack. # sub create_subrecord { my $num_records = shift(@_); my $start_offset = shift(@_); # By default, move all records to subrecord if($num_records<=0) { $num_records = $#records+1; $bottom = 0; } #Create directory, if needed if($subrecord_number<=0) { if($file_subdirectory ne "") { if( !(-e $file_subdirectory) ) { mkdir $file_subdirectory or die "Can't create directry $file_subdirectory: $!"; } $file_subdirectory = $file_subdirectory."/"; } } #Build filename my $current_record_empty = ((keys %current) == 0); if($current_record_empty) { $subrecord_filename = "00index"; } else { $subrecord_filename = "file_".$subrecord_number++; } $subrecord_filename_with_suffix = $subrecord_filename.".html"; #Create text for a link to the file just created. if(!$current_record_empty) { if($current{"buffer"} =~ /(\n|.)*(\n|.)*<\/a>/) { $current{"buffer"} =~ s/((\n|.)*)href=""/$1href="$subrecord_filename_with_suffix"/; } else { $current{"buffer"} = sprintf "%s\n", $current{"buffer"}; } } # Move records if($current_record_empty) { @outfile_subrecord = splice @records, $start_offset+$bottom, $num_records; } else { @outfile_subrecord = splice @records, $start_offset+$bottom, $num_records, {%current}; %current = (); } #Open file open OUTFILE, "> ".$file_subdirectory.$subrecord_filename_with_suffix or die "Can't open $subrecord_filename_with_suffix : $!"; html_open(); #Write records for my $aaa(reverse(0..$#outfile_subrecord)) { print OUTFILE $outfile_subrecord[$aaa]{"buffer"}; } #Close file html_close(); close OUTFILE; $bottom += $start_offset; } ################################################################# ### FUNCTIONS TO HANDLE FIELDS OF BITS AND MUTIBYTE REGISTERS ### ################################################################# # These functions extract fields of bits from raw reads # and writes to registers. Fields may be less than a byte # in length (multipel fields packed into a byte) or span # multiple registers. A subrecord will be created for # each multibyte field or byte of fields. # Allow a string to be generated from a formula (similar to # a spreadsheet. Formulas are marked by a string starting with # an equals sign ('='). # # Inputs: # $string: The string to be evaluated. If the string is of the form # "=...", it will be evaluated, otherwise it will be returned # unchanged. Formulas may include any gloabal variables # and the local variable '$field'. # $field: Typically set this to the value of the field currently # being processed. # Returns: The evaluated string. # sub execute { my $string = shift(@_); my $field = shift(@_); if($string =~ /^=(.*)/) { $string = eval $1; warn $@ if $@; } return $string; } # Extract the value of a (posssibly multibit) field from # a byte and create an annotation for it. If the value of the # field is different from the last time it was accessed # the annotation will be preceeeded by an asterisk ('*'). # # Inputs: # $value : The value of the byte containing the field (an integer). # $lsb : The bit number of the lsb of the bit field. The lsb # of the byte is '0' and the msb of the byte is '7'. # $width : The width of the bit field in bits. # $title : A string which always starts the annotation. # $register: The name of the field (a string) # @comment : All remaining arguments form an array of comments/formulas. # The value of the field will be used as in index of this # array, so the comment printed will depend on the value # of the field. If the array has length one that # comment/formula will be used for all values. Any # comment/formula may include a single format string # (eg. "%i"), which will be replaced with the value of the field. # # Globals: @gl841_field: an array of past values of each field for the gl841 # # Returns: An annotation for the bit field (as a string). # sub comment_multibit_field { my $value = shift(@_); my $lsb = shift(@_); my $width = shift(@_); my $title = shift(@_); my $register = shift(@_); my @comment = @_; # list of comments for each value, or a format field my $temp_buffer = "\n "; my $mask = ((1 << $width)-1) << $lsb; my $field = ($value&$mask) >> $lsb; if($field != $gl841_field{$register}) { $temp_buffer .= "*"; } else { $temp_buffer .= " "; } my $field_name = "field_".$register; $current{$field_name} = $field; $gl841_field{$register} = $field; $temp_buffer .= $title; if(@comment > 1) { $temp_buffer .= execute($comment[$field], $field); } else { $temp_buffer .= sprintf( execute($comment[0], $field), $field); } return($temp_buffer); } # Test if a record is a register read/write for a # given GL841 register. If so create a subrecord # for the gl841 register assuming it contains multiple # fields (possibly multibit fields). The subrecord will # be pointed to by an annotation that explains the contents # of the register and indicates, with a '*', any of the # fields have been changed. # # Inputs: # $address : The value of the register. # $register : The name of the register (a string). # $format : A printf formay string, which will be used to print the value # of the register. # $commnt : A string describing the register. # $bit_fields: All remaining arguments form an array which describes # each of the fields contained in the register. Each # field is described by an array containing: # a) The bit number of the lsb of the bit field. The lsb # of the byte is '0' and the msb of the byte is '7'. # b) The width of the bit field in bits. # c) A string which describes the bit field. # d) The name of the bit field (a string) # e) An array of comments/formulas, one for each # value of the bit field. If the array has length # one that comment/formula will be used for all values. # Any comment/formula may include a single format # string (eg. "%i"), which will be replaced with the # value of the field. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Globals: @gl841_field : an array of past values of each field for the gl841 # @gl841_register: an array of past values of each register for the gl841 # # Returns: 1 if the record was a read/write to the given gl841 register # and a subrecord was created. # 0 otherwise. # sub bit_field_register_read_write { my $address = shift(@_); # address my $register = shift(@_); # register name my $format = shift(@_); # printf format for register contents my $comment = shift(@_); # comment, including a field for the value. (Write/Read ...comment) my @bit_fields = @_; # descriptors for each bit field if ( $records[$bottom]{"address"} == $address ) { if ( $records[$bottom]{"type"} eq "gl841_register_read" ) { $current{"type"} = "gl841_".$register."_read"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"data"} = $records[$bottom]{"data"}; $current{"register"} = $register; my $value = $current{"data"}; my $temp_buffer = sprintf "[gl841:$register] -> $format Read $comment.", $current{"data"}, $current{"data"}; foreach $bf(@bit_fields) { $temp_buffer .= comment_multibit_field($value, @{$bf}); } $current{"buffer"} = std_text($temp_buffer); $gl841_register[$records[$bottom]{"address"}] = $records[$bottom]{"data"}; create_subrecord(1); return 1; } elsif ( $records[$bottom]{"type"} eq "gl841_register_write" ) { $current{"type"} = "gl841_".$register."_write"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"data"} = $records[$bottom]{"data"}; $current{"register"} = $register; my $value = $current{"data"}; my $temp_buffer = sprintf " $format -> [gl841:$register] Write $comment.", $current{"data"}, $current{"data"}; foreach $bf(@bit_fields) { $temp_buffer .= comment_multibit_field($value, @{$bf}); } $current{"buffer"} = std_text($temp_buffer); $gl841_register[$records[$bottom]{"address"}] = $records[$bottom]{"data"}; create_subrecord(1); return 1; } } return 0; } # Test if a sequence of records is a read/write for a # given (possibly multibyte) field within the GL841. # If so create a subrecord for the gl841 field. If the # field covers multiple bytes, mutiple register read/writes # will be grouped together. The subrecord will # be pointed to by an annotation that explains the contents # of the field. # # If only a subset of the registers which comprise the field # are written to/read from, the missing bytes will be filled # in from past reads/writes. # # Inputs: # $start : The address of the register containing the most significant byte of the field. # $end : The address of the register containing the least significant byte of the field. # The MSB is assumed to come before the LSB, so $start<=$end. # $mask : A mask used to remove any unused bits in the MSB. Zeros indicate unused # bits. # $format : A printf formay string, which will be used to print the value # of the field. # $register : The name of the field (a string) # $comment : A string describing the register. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Globals: @gl841_field : an array of past values of each field for the gl841 # @gl841_register: an array of past values of each register for the gl841 # # Returns: 1 if the record was a read/write to the given gl841 field # and a subrecord was created. # 0 otherwise. # sub multi_byte_register_read_write { my $start = shift(@_); # address of MSB (lowest) my $end = shift(@_); # address of LSB (highest) my $mask = shift(@_); # bit mask for MSB my $register = shift(@_); # register name my $format = shift(@_); # printf format for register contents my $comment = shift(@_); # comment, including a field for the value. (Write/Read ...comment) if($end == $start) { $base = $bottom; } else { $base = $bottom+1; } if ( $records[$base]{"address"} >= $start and $records[$base]{"address"} <= $end ) { # Check if address is in range my $last_byte; my $byte_count; if($records[$base]{"type"} eq "gl841_register_read") { # Is it a read or write? my @byte_done; my $index = $end-$records[$base]{"address"}; $byte_done[$index] = 1; my $byte; if($records[$base]{"address"}==$start) { $byte = $records[$base]{"data"} & $mask; } else { $byte = $records[$base]{"data"}; } my $data = $byte<< (8*$index); if($base>$bottom) { if( substr($records[$bottom]{"type"}, 0, 4) eq "urb_" ) { return 0; #bail as there is a possibilty we can make a longer byte sequence next pass since the partially build register read may relevant. } my $possible_index = -1; if( $records[$bottom]{"type"} eq "gl841_register_read" and $records[$bottom]{"address"} >= $start and $records[$bottom]{"address"} <= $end) { $possible_index = $end-$records[$bottom]{"address"}; } my $byte_offset = $base+1; while($byte_done[$end-$records[$byte_offset]{"address"}] == 0 and $records[$byte_offset]{"type"} eq "gl841_register_read" and $records[$byte_offset]{"address"} >= $start and $records[$byte_offset]{"address"} <= $end) { $index = $end-$records[$byte_offset]{"address"}; $byte_done[$index] = 1; if($records[$byte_offset]{"address"}==$start) { $byte = $records[$byte_offset]{"data"} & $mask; } else { $byte = $records[$byte_offset]{"data"}; } $data += $byte<< (8*$index); $byte_offset++; } $last_byte = $byte_offset-1; $byte_count = $byte_offset-$base; if($possible_index>=0 and not $byte_done[$possible_index]) { return 0; #bail as we can make a longer byte sequence next pass. } } else { $last_byte = $base; $byte_count = 1; } $current{"data"} = $data; $current{"type"} = "gl841_".$register."_read"; $current{"timestamp"} = $records[$last_byte]{"timestamp"}; $current{"number"} = $records[$last_byte]{"number"}; if($byte_count>1) { $current{"last_number"} = $records[$base]{"last_number"}; } $current{"register"} = $register; $gl841_field{$register} = $current{"data"}; $current{"buffer"} = std_text(sprintf "[gl841:$register] -> $format Read ".execute($comment,$current{"data"})." from the GL841.", $current{"data"}, $current{"data"}); create_subrecord($byte_count, $base-$bottom); return 1; } elsif($records[$base]{"type"} eq "gl841_register_write") { my @byte_done; my $index = $end-$records[$base]{"address"}; $byte_done[$index] = 1; my $byte; if($records[$base]{"address"}==$start) { $gl841_register[$records[$base]{"address"}] = $records[$base]{"data"} & $mask; } else { $gl841_register[$records[$base]{"address"}] = $records[$base]{"data"}; } if($base>$bottom) { if( substr($records[$bottom]{"type"}, 0, 4) eq "urb_" ) { return 0; #bail as there is a possibilty we can make a longer byte sequence next pass since the partially build register read may relevant. } my $possible_index = -1; if( $records[$bottom]{"type"} eq "gl841_register_write" and $records[$bottom]{"address"} >= $start and $records[$bottom]{"address"} <= $end) { $possible_index = $end-$records[$bottom]{"address"}; } my $byte_offset = $base+1; while($byte_done[$end-$records[$byte_offset]{"address"}] == 0 and $records[$byte_offset]{"type"} eq "gl841_register_write" and $records[$byte_offset]{"address"} >= $start and $records[$byte_offset]{"address"} <= $end) { $index = $end-$records[$byte_offset]{"address"}; $byte_done[$index] = 1; if($records[$byte_offset]{"address"}==$start) { $gl841_register[$records[$byte_offset]{"address"}] = $records[$byte_offset]{"data"} & $mask; } else { $gl841_register[$records[$byte_offset]{"address"}] = $records[$byte_offset]{"data"}; } $byte_offset++; } $last_byte = $byte_offset-1; $byte_count = $byte_offset-$base; if($possible_index>=0 and not $byte_done[$possible_index]) { return 0; #bail as we can make a longer byte sequence next pass. } } else { $last_byte = $base; $byte_count = 1; } $current{"data"} = $gl841_register[$start] & $mask; for(my $address=$start+1; $address<=$end; $address++) { $current{"data"} = 256*$current{"data"} + $gl841_register[$address]; } $current{"type"} = "gl841_".$register."_write"; $current{"timestamp"} = $records[$last_byte]{"timestamp"}; $current{"number"} = $records[$last_byte]{"number"}; if($byte_count>1) { $current{"last_number"} = $records[$base]{"number"}; } $current{"register"} = $register; $gl841_field{$register} = $current{"data"}; $current{"buffer"} = std_text(sprintf " $format -> [gl841:$register] Write ".execute($comment,$current{"data"})." to the GL841.", $current{"data"}, $current{"data"}); create_subrecord($byte_count, $base-$bottom); return 1; } } return 0; } ################################################################# ############## FUNCTIONS TO HANDLE WAITING FOR AN EVENT ######### ################################################################# # These functions handle the case when the device is doing # something and the software is looping, checking status, # until the device has completed its task. # Test if a sequence of records corresponds to the # scanner waiting for some event to happen (such as a bit # being set in a register). If so create a subrecord # containing the sequence of register reads. The subrecord # will be pointed to by an annotation that explains the # purpose of the sequence of repeated reads. # # A 'wait sequence' will be of the form: # # read condition 'x' # read condition 'x' # . # . # read condition 'x' # read condition 'y' # some other action # # Inputs: # $search_type : The type of records we are looking for (typically # a read for a particular register in the gl841). # $search_mask : A mask indicating which bits to ignore in the # register (0 = ignore the bit). # $search_pattern : A pattern of bits (correspoinding to the mask) # which indicates that the condition has been meet. # $type : The type of the newly created record, which is to # replace the sequence of reads. # $comment : A comment describing what the scanner was waiting for. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a 'wait sequence' was found and a subrecord was created. # 0 otherwise. # sub wait_for_event { my $search_type = shift(@_); my $search_mask = shift(@_); my $search_pattern = shift(@_); my $type = shift(@_); my $comment = shift(@_); if( $records[$bottom+2]{"type"} eq $search_type and ($records[$bottom+2]{"data"}&$search_mask) != $search_pattern and $records[$bottom+1]{"type"} eq $search_type and ($records[$bottom+1]{"data"}&$search_mask) == $search_pattern and not( $records[$bottom]{"type"} eq $search_type or substr($records[$bottom]{"type"}, 0, 4) eq "urb_") ) { my $byte_offset = $bottom+3; while($records[$byte_offset]{"type"} eq $search_type and ($records[$byte_offset]{"data"}&$search_mask) != $search_pattern) { $byte_offset++; } $last_byte = $byte_offset-1; $byte_count = $byte_offset-$bottom-1; $current{"timestamp"} = $records[$last_byte]{"timestamp"}; $current{"number"} = $records[$last_byte]{"number"}; $current{"last_number"} = $records[$bottom+1]{"last_number"}; if($byte_count>1) { $current{"type"} = $type; $current{"count"} = $byte_count; $current{"buffer"} = std_text($comment); } create_subrecord($byte_count, 1); return 1; } return 0; } # Test if a sequence of records corresponds to the # scanner waiting for a bit to be set/reset in a # register. If so create a subrecord containing # the sequence of register reads. The subrecord # will be pointed to by an annotation that explains the # purpose of the sequence of repeated reads. # # A 'wait sequence' will be of the form: # # read condition bit z = 'x' # read condition bit z = 'x' # . # . # read condition bit z = 'x' # read condition bit z = 'y' # some other action # # Inputs: # $search_type : The type of records we are looking for (typically # a read for a particular register in the gl841). # $search_position: The bit position of the bit in the register. # $search_pattern : The value (0 or 1) of the bit which indicates # that the condition has been meet. # $type : The type of the newly created record, which is to # replace the sequence of reads. # $comment : A comment describing what the scanner was waiting for. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a 'wait sequence' was found and a subrecord was created. # 0 otherwise. # sub wait_for_bit { my $search_type = shift(@_); my $search_position = shift(@_); my $search_bit = shift(@_); my $type = shift(@_); my $comment = shift(@_); if($search_bit!=0) { $search_bit = 1; } return wait_for_event($search_type, (1<<$search_position), ($search_bit<<$search_position), $type, $comment ); } ################################################################# #### CREATE RECORDS FOR HIGH LEVLE FUNCTIONS OF THE LIDE 60 ##### ################################################################# # Annotate bulk transfers sub process_bulk_transfers { if( $records[$bottom]{"type"} eq "gl841_write_unknown2" and $records[$bottom]{"data"}[0] == 0x00 and $records[$bottom]{"data"}[1] == 0x00 and $records[$bottom]{"data"}[2] == 0x82 and $records[$bottom]{"data"}[3] == 0x00 ) { $current{"type"} = "gl841_setup_bulk_read"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"req_type"} = $records[$bottom]{"data"}[0]; $current{"req"} = $records[$bottom]{"data"}[1]; $current{"req_value"} = $records[$bottom]{"data"}[2]; $current{"req_index"} = $records[$bottom]{"data"}[3]; $current{"data"} = $records[$bottom]{"data"}[4] + ($records[$bottom]{"data"}[5]<<8 ) + ($records[$bottom]{"data"}[6]<<16) + ($records[$bottom]{"data"}[7]<<24); $current{"buffer"} = std_text(sprintf "Setup to read %i bytes from endpoint 2 (bulk)", $current{"data"} ); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "gl841_write_unknown2" and $records[$bottom]{"data"}[0] == 0x01 and $records[$bottom]{"data"}[1] == 0x00 and ($records[$bottom]{"data"}[2] == 0x82 or $records[$bottom]{"data"}[2] == 0x00) and $records[$bottom]{"data"}[3] == 0x00 ) { $current{"type"} = "gl841_setup_bulk_write"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"req_type"} = $records[$bottom]{"data"}[0]; $current{"req"} = $records[$bottom]{"data"}[1]; $current{"req_value"} = $records[$bottom]{"data"}[2]; $current{"req_index"} = $records[$bottom]{"data"}[3]; $current{"data"} = $records[$bottom]{"data"}[4] + ($records[$bottom]{"data"}[5]<<8 ) + ($records[$bottom]{"data"}[6]<<16) + ($records[$bottom]{"data"}[7]<<24); $current{"buffer"} = std_text(sprintf "Setup to write %i bytes to endpoint %i (bulk)", $current{"data"}, $current{"req_value"}&0x0f ); create_subrecord(1); } else { return 0; } return 1; } # Annotate high level LiDE 60 functions sub process_lide60_high_level { if(wait_for_bit("gl841_scanner_status_read", 3, 0, "lide60_detect_home", "Wait for scanner to reach home position. ([0x41] bit 3)==1")){ } elsif(wait_for_bit("gl841_scanner_status_read", 6, 0, "lide60_detect_image_data", "Wait for image buffer to contain data. ([0x41] bit 6)==1")){ } elsif(wait_for_bit("gl841_scanner_status_read", 0, 0, "lide60_detect_motor_stopped", "Wait for motor to stop processing. ([0x41] bit 0)==1")){ } elsif(wait_for_bit("gl841_motor_state_read", 0, 0, "lide60_detect_motor_stopped_long", "Wait for motor to stop processing (with other checks??). ([0x41] bit 0)==1")){ } elsif (# Multiple Bulk Reads $records[$bottom]{"type"} eq "gl841_bulk_read" and $records[$bottom]{"data_length"} != 61440 ) { my $idle_counter = 1; my $data_length = $records[$bottom]{"data_length"}; while($records[$idle_counter]{"type"} eq "gl841_bulk_read" and $records[$idle_counter]{"data_length"} == 61440) { $data_length += $records[$idle_counter]{"data_length"}; $idle_counter++; } $idle_counter--; $current{"type"} = "gl841_multiple_bulk_read"; $current{"timestamp"} = $records[$idle_counter]{"timestamp"}; $current{"number"} = $records[$idle_counter]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"data_length"} = $data_length; $current{"buffer"} = std_text(sprintf "MULTIPLE BULK READS (%s bytes)", $current{"data_length"}); create_subrecord($idle_counter+1); } elsif (# Read block from image buffer $records[$bottom+2]{"type"} eq "gl841_register_read_address_only" and $records[$bottom+2]{"address"} == 0x45 and $records[$bottom+1]{"type"} eq "gl841_setup_bulk_read" and $records[$bottom]{"type"} eq "gl841_multiple_bulk_read" ) { $current{"type"} = "gl841_read_image_buffer"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"data_length"} = $records[$bottom+1]{"data"}; if($records[$bottom]{"data_length"} < $records[$bottom+1]{"data"}) { $current{"buffer"} = std_text(sprintf "NOT ENOUGH DATA (packet:%i < length:%i).", $records[$bottom]{"data_length"}, $records[$bottom+1]{"data"}); } else { $current{"buffer"} = std_text(sprintf "Read data from image buffer (%s bytes)", $current{"data_length"}); } create_subrecord(3); } elsif (# Write block to scanner memory (image buffer or gamma table) $records[$bottom+2]{"type"} eq "gl841_register_read_address_only" and ($records[$bottom+2]{"address"} == 0x3c or $records[$bottom+2]{"address"} == 0x28) and $records[$bottom+1]{"type"} eq "gl841_setup_bulk_write" and $records[$bottom]{"type"} eq "gl841_bulk_write" ) { $current{"type"} = "gl841_write_buffer"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"address"} = $records[$bottom+2]{"address"}; $current{"data_length"} = $records[$bottom+1]{"data"}; if($records[$bottom]{"data_length"} < $records[$bottom+1]{"data"}) { $current{"buffer"} = std_text(sprintf "NOT ENOUGH DATA (packet:%i < length:%i).", $records[$bottom]{"data_length"}, $records[$bottom+1]{"data"}); } else { if($current{"address"}==0x3c) { $current{"buffer"} = std_text(sprintf "Write data to image buffer (%s bytes)", $current{"data_length"}); } else { $current{"buffer"} = std_text(sprintf "Write data to gamma table (%s bytes)", $current{"data_length"}); } } create_subrecord(3); } elsif (# Write data block to scanner memory (image buffer or gamma table) @@@@@@@ $records[$bottom+3]{"type"} eq "gl841_start_stop_bulk" and ($records[$bottom+2]{"type"} eq "gl841_ramaddr_write" or $records[$bottom+2]{"type"} eq "gl841_gmmaddr_write") and $records[$bottom+1]{"type"} eq "gl841_write_buffer" and $records[$bottom]{"type"} eq "gl841_start_stop_bulk" ) { if($records[$bottom+2]{"type"} eq "gl841_ramaddr_write") { $current{"type"} = "gl841_write_dram_table"; } else { $current{"type"} = "gl841_write_gamma_table"; } $current{"timestamp"} = $records[$bottom+3]{"timestamp"}; $current{"number"} = $records[$bottom+3]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"address"} = $records[$bottom+2]{"data"}*16; $current{"data_length"} = $records[$bottom+1]{"data_length"}; my $temp_buffer = "Write "; if($current{"type"} eq "gl841_write_dram_table") { if($current{"address"} == 0x00000) { $temp_buffer .= "red/grey channel shading mapping table"; } elsif($current{"address"} == 0x05500) { $temp_buffer .= "green channel shading mapping table (1200dpi)"; } elsif($current{"address"} == 0x0aa00) { $temp_buffer .= "blue channel shading mapping table (1200dpi)"; } elsif($current{"address"} == 0x02a00) { $temp_buffer .= "green channel shading mapping table (600dpi)"; } elsif($current{"address"} == 0x05400) { $temp_buffer .= "blue channel shading mapping table (600dpi)"; } elsif($current{"address"} == 0x0a800) { $temp_buffer .= "green channel shading mapping table (2400dpi)"; } elsif($current{"address"} == 0x15000) { $temp_buffer .= "blue channel shading mapping table (2400dpi)"; } elsif($current{"address"} == 0x10000) { $temp_buffer .= "motor slope curve mapping table 1 (1200dpi)"; } elsif($current{"address"} == 0x10200) { $temp_buffer .= "motor slope curve mapping table 2 (1200dpi)"; } elsif($current{"address"} == 0x10400) { $temp_buffer .= "motor slope curve mapping table 3 (1200dpi)"; } elsif($current{"address"} == 0x10600) { $temp_buffer .= "motor slope curve mapping table 4 (1200dpi)"; } elsif($current{"address"} == 0x10800) { $temp_buffer .= "motor slope curve mapping table 5 (1200dpi)"; } elsif($current{"address"} == 0x20000) { $temp_buffer .= "motor slope curve mapping table 1 (2400dpi)"; } elsif($current{"address"} == 0x20200) { $temp_buffer .= "motor slope curve mapping table 2 (2400dpi)"; } elsif($current{"address"} == 0x20400) { $temp_buffer .= "motor slope curve mapping table 3 (2400dpi)"; } elsif($current{"address"} == 0x20600) { $temp_buffer .= "motor slope curve mapping table 4 (2400dpi)"; } elsif($current{"address"} == 0x20800) { $temp_buffer .= "motor slope curve mapping table 5 (2400dpi)"; } elsif($current{"address"} == 0x08000) { $temp_buffer .= "motor slope curve mapping table 1 (600dpi)"; } elsif($current{"address"} == 0x08200) { $temp_buffer .= "motor slope curve mapping table 2 (600dpi)"; } elsif($current{"address"} == 0x08400) { $temp_buffer .= "motor slope curve mapping table 3 (600dpi)"; } elsif($current{"address"} == 0x08600) { $temp_buffer .= "motor slope curve mapping table 4 (600dpi)"; } elsif($current{"address"} == 0x08800) { $temp_buffer .= "motor slope curve mapping table 5 (600dpi)"; } $temp_buffer .= " to image buffer"; } else { $temp_buffer .= " to gamma ram"; } $temp_buffer .= sprintf " (%s bytes at address 0x%05x)", $current{"data_length"}, $current{"address"}; $current{"buffer"} = std_text($temp_buffer); create_subrecord(4); } elsif(# Read multiple blocks from image buffer $records[$bottom+1]{"type"} eq "gl841_read_image_buffer" and not( $records[$bottom]{"type"} eq "gl841_read_image_buffer" or $records[$bottom]{"type"} eq "gl841_register_read_address_only" or substr($records[$bottom]{"type"}, 0, 4) eq "urb_") ) { my $idle_counter = 2; my $data_length = $records[$bottom+1]{"data_length"}; while($records[$idle_counter]{"type"} eq "gl841_read_image_buffer") { $data_length += $records[$idle_counter]{"data_length"}; $idle_counter++; } $idle_counter--; $current{"timestamp"} = $records[$idle_counter]{"timestamp"}; $current{"number"} = $records[$idle_counter]{"number"}; $current{"last_number"} = $records[$bottom+1]{"last_number"}; $current{"data_length"} = $data_length; $current{"buffer"} = std_text(sprintf "Multiple reads from image buffer (%s bytes)", $current{"data_length"}); create_subrecord($idle_counter, 1); } elsif( $records[$bottom+3]{"type"} eq "gl841_gpio13_18_led_control_write" and $records[$bottom+3]{"data"} == 0x00 and $records[$bottom+2]{"type"} eq "gl841_motor_gpio17_18_write" and ($records[$bottom+2]{"data"}&0xfc) == 0x00 and $records[$bottom+1]{"type"} eq "gl841_gpio16_9_dir_write" and $records[$bottom+1]{"data"} == 0xef and $records[$bottom]{"type"} eq "gl841_gpio16_9_data_write" and ($records[$bottom]{"data"}&0x10) == 0x00 ) { $current{"type"} = "lide60_setup_gpio"; $current{"timestamp"} = $records[$bottom+3]{"timestamp"}; $current{"number"} = $records[$bottom+3]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; my $temp_buffer = " 0b "; for my $i(0..1) { if( ($records[$bottom+2]{"data"}&(0x01 << (1-$i))) ) { $temp_buffer .= "1"; } else { $temp_buffer .= "0"; } } for my $i(0..7) { if(($i&3)==0) { $temp_buffer .= " "; } if( ($records[$bottom]{"data"}&(0x01 << (7-$i))) ) { $temp_buffer .= "1"; } else { $temp_buffer .= "0"; } } $temp_buffer .= " -> [gpio18..9] set GPIO18-9 to known state (includes pins which control motor?)"; $current{"buffer"} = std_text($temp_buffer); create_subrecord(4); # } elsif () { } else { return process_bulk_transfers(); } return 1; } # Try to group related fields together sub process_gl841_register_groups { if( # Exposure registers $records[$bottom+3]{"type"} eq "gl841_expr_write" and $records[$bottom+2]{"type"} eq "gl841_expg_write" and $records[$bottom+1]{"type"} eq "gl841_expb_write" ) { $current{"type"} = "gl841_exposure_time_write"; $current{"timestamp"} = $records[$bottom+3]{"timestamp"}; $current{"number"} = $records[$bottom+3]{"number"}; $current{"last_number"} = $records[$bottom+1]{"number"}; $current{"data_r"} = $records[$bottom+3]{"data"}; $current{"data_g"} = $records[$bottom+2]{"data"}; $current{"data_b"} = $records[$bottom+1]{"data"}; $current{"buffer"} = std_text(sprintf " Set exposure time (%i, %i, %i) -> (R,G,B).", $current{"data_r"}, $current{"data_g"}, $current{"data_b"}); create_subrecord(3, 1); } else { return process_lide60_high_level(); } return 1; } ################################################################# ####### CREATE RECORDS FOR WHEN THE SCANNER IS IDLE ############# ################################################################# # Annotations for when the scanner is idle # Figure out when the scanner is sitting there doing nothing. # When thescanner is idle, the driver loops while # inspecting the state of the GPIO pins, presumambly looking # for butons pressed on the front panel of the scanner. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a sequence of records was recognised and grouped into a subrecord. # 0 otherwise. # sub process_lide60_idle { if( $records[$bottom+1]{"type"} eq "gl841_gpio8_1_data_read" and $records[$bottom+1]{"data"} == 0xbf and not( ($records[$bottom]{"type"} eq "gl841_gpio8_1_data_read" and $records[$bottom]{"data"} == 0xbf) or substr($records[$bottom]{"type"}, 0, 4) eq "urb_") ) { my $idle_counter = 2; while($records[$idle_counter]{"type"} eq "gl841_gpio8_1_data_read" and $records[$idle_counter]{"data"} == 0xbf) { $idle_counter++; } $idle_counter--; $current{"timestamp"} = $records[$idle_counter]{"timestamp"}; $current{"number"} = $records[$idle_counter]{"number"}; $current{"last_number"} = $records[$bottom+1]{"last_number"}; if($idle_counter>1) { $current{"type"} = "lide60_idle"; $current{"count"} = $idle_counter; $current{"buffer"} = std_text(sprintf "Scanner Idle for %.1fs. Monitoring GPIO for buttons pressed. *********************************************", ($records[$bottom]{"timestamp"}-$current{"timestamp"})/1000); } else { $current{"type"} = "lide60_check_gpio"; $current{"buffer"} = std_text("Check GPIO for buttons."); } create_subrecord($idle_counter, 1); } else { return process_gl841_register_groups(); } return 1; } ################################################################# ####### CREATE RECORDS CORRESPONDING TO WM8199 FRONT END ######## ################################################################# # Analyse at the level of operations on the WM8199 analog front end # Convert WM8199 register addresses into meaningful strings. # # Inputs: $address: The address of the WM8199 register (an integer) # # Returns: A string corresponding to the name of the register. # sub wm8199_register { my $address = shift(@_); my @wm8199_registers1_12 = ("SetupReg1", "SetupReg2", "SetupReg3", "SoftwareReset", "Auto-cycle Reset", "SetupReg4", "RevisionNumber", "SetupReg5", "SetupReg6", "Reserved", "Reserved", "Reserved" ); my @wm8199_registers32_35 = ("DAC Value (Red)", "DAC Value (Green)", "DAC Value (Blue)", "DAC Value (RGB)" ); my @wm8199_registers40_43 = ("PGA Gain (Red)", "PGA Gain (Green)", "PGA Gain (Blue)", "PGA Gain (RGB)" ); if($address>=1 and $address<=12 ) { return $wm8199_registers1_12[$address-1]; } elsif($address>=32 and $address<=35 ) { return $wm8199_registers32_35[$address-32]; } elsif($address>=40 and $address<=43 ) { return $wm8199_registers40_43[$address-40]; } else { return (sprintf "UNKNOWN%i",$address); } } # Analyse at the level of operations on the WM8199 analog front end # chip. Group manipulation of fields within the GL841 together # to form records corresponding to carrying out operations on # the WM8199. # For example: read WM8199 register, write 8199 register, # set up DAC and so on. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a sequence of records was recognised and grouped into a subrecord. # 0 otherwise. # sub process_wm8199 { if( $records[$bottom+1]{"type"} eq "gl841_fewra_write" and $records[$bottom ]{"type"} eq "gl841_fewrdata_write" ) { $current{"type"} = "wm8199_register_write"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"address"} = $records[$bottom+1]{"data"}; $current{"data"} = $records[$bottom]{"data"}; $current{"register"} = wm8199_register($current{"address"}); $current{"buffer"} = std_text(sprintf " 0x%02x -> [0x%02x] Write to %s register in the WM8199", $current{"data"}, $current{"address"}, $current{"register"}); create_subrecord(2); } elsif( $records[$bottom+1]{"type"} eq "gl841_fewra_write" and $records[$bottom]{"type"} eq "gl841_ferddata_read" ) { $current{"type"} = "wm8199_register_read"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"address"} = $records[$bottom+1]{"data"}; $current{"data"} = $records[$bottom]{"data"}; $current{"register"} = wm8199_register($current{"address"}); $current{"buffer"} = std_text(sprintf " 0x%02x -> [0x%02x] Read from %s register in the WM8199", $current{"data"}, $current{"address"}, $current{"register"}); create_subrecord(2); } elsif( $records[$bottom+2]{"type"} eq "wm8199_register_write" and $records[$bottom+2]{"register"} eq "SetupReg1" and $records[$bottom+2]{"data"} == 0x3d and $records[$bottom+1]{"type"} eq "wm8199_register_write" and $records[$bottom+1]{"register"} eq "SetupReg2" and $records[$bottom+1]{"data"} == 0x08 and $records[$bottom]{"type"} eq "wm8199_register_write" and $records[$bottom]{"register"} eq "SetupReg3" and $records[$bottom]{"data"} == 0x00 ) { $current{"type"} = "wm8199_setup_123"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; @{$current{"data"}}[0] = $records[$bottom+2]{"data"}; @{$current{"data"}}[1] = $records[$bottom+1]{"data"}; @{$current{"data"}}[2] = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf "Setup WM8199 UNKNOWN (SetupReg1=0x%02x, SetupReg2=0x%02x, SetupReg3=0x%02x)", @{$current{"data"}}[0], @{$current{"data"}}[1], @{$current{"data"}}[2]); create_subrecord(3); } elsif( $records[$bottom+2]{"type"} eq "wm8199_register_write" and $records[$bottom+2]{"register"} eq "DAC Value (Red)" and $records[$bottom+2]{"data"} == 0xe1 and $records[$bottom+1]{"type"} eq "wm8199_register_write" and $records[$bottom+1]{"register"} eq "DAC Value (Green)" and $records[$bottom+1]{"data"} == 0xe1 and $records[$bottom]{"type"} eq "wm8199_register_write" and $records[$bottom]{"register"} eq "DAC Value (Blue)" and $records[$bottom]{"data"} == 0xe1 ) { $current{"type"} = "wm8199_setup_dac"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; @{$current{"data"}}[0] = $records[$bottom+2]{"data"}; @{$current{"data"}}[1] = $records[$bottom+1]{"data"}; @{$current{"data"}}[2] = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf "Set DAC RGB channels of WM8199 to (0x%02x,0x%02x,0x%02x).", @{$current{"data"}}[0], @{$current{"data"}}[1], @{$current{"data"}}[2]); create_subrecord(3); } elsif( $records[$bottom+2]{"type"} eq "wm8199_register_write" and $records[$bottom+2]{"register"} eq "PGA Gain (Red)" and $records[$bottom+2]{"data"} == 0x93 and $records[$bottom+1]{"type"} eq "wm8199_register_write" and $records[$bottom+1]{"register"} eq "PGA Gain (Green)" and $records[$bottom+1]{"data"} == 0x93 and $records[$bottom]{"type"} eq "wm8199_register_write" and $records[$bottom]{"register"} eq "PGA Gain (Blue)" and $records[$bottom]{"data"} == 0x93 ) { $current{"type"} = "wm8199_setup_pga"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; @{$current{"data"}}[0] = $records[$bottom+2]{"data"}; @{$current{"data"}}[1] = $records[$bottom+1]{"data"}; @{$current{"data"}}[2] = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf "Set PGA Gain RGB channels of WM8199 to (0x%02x,0x%02x,0x%02x).", @{$current{"data"}}[0], @{$current{"data"}}[1], @{$current{"data"}}[2]); create_subrecord(3); } elsif( $records[$bottom+2]{"type"} eq "wm8199_register_write" and $records[$bottom+2]{"register"} eq "UNKNOWN36" and $records[$bottom+2]{"data"} == 0x00 and $records[$bottom+1]{"type"} eq "wm8199_register_write" and $records[$bottom+1]{"register"} eq "UNKNOWN37" and $records[$bottom+1]{"data"} == 0x00 and $records[$bottom]{"type"} eq "wm8199_register_write" and $records[$bottom]{"register"} eq "UNKNOWN38" and $records[$bottom]{"data"} == 0x00 ) { $current{"type"} = "wm8199_setup_unknown_rgb"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; @{$current{"data"}}[0] = $records[$bottom+2]{"data"}; @{$current{"data"}}[1] = $records[$bottom+1]{"data"}; @{$current{"data"}}[2] = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf "Set unknown RGB channels of WM8199 to (0x%02x,0x%02x,0x%02x).", @{$current{"data"}}[0], @{$current{"data"}}[1], @{$current{"data"}}[2]); create_subrecord(3); } elsif( $records[$bottom+2]{"type"} eq "wm8199_setup_123" and $records[$bottom+1]{"type"} eq "wm8199_setup_dac" and $records[$bottom]{"type"} eq "wm8199_setup_pga" ) { $current{"type"} = "wm8199_setup_short"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"buffer"} = std_text("Setup WM8199 UNKNOWN, Offset DACs and PGA Gains"); create_subrecord(3); } elsif( $records[$bottom+9]{"type"} eq "wm8199_register_write" and $records[$bottom+9]{"register"} eq "SoftwareReset" and $records[$bottom+9]{"data"} == 0x00 and $records[$bottom+8]{"type"} eq "wm8199_register_write" and $records[$bottom+8]{"register"} eq "UNKNOWN0" and $records[$bottom+8]{"data"} == 0x00 and $records[$bottom+7]{"type"} eq "wm8199_setup_123" and $records[$bottom+6]{"type"} eq "wm8199_register_write" and $records[$bottom+6]{"register"} eq "Auto-cycle Reset" and $records[$bottom+6]{"data"} == 0x05 and $records[$bottom+5]{"type"} eq "wm8199_register_write" and $records[$bottom+5]{"register"} eq "SetupReg4" and $records[$bottom+5]{"data"} == 0x00 and $records[$bottom+4]{"type"} eq "wm8199_register_write" and $records[$bottom+4]{"register"} eq "SetupReg5" and $records[$bottom+4]{"data"} == 0x19 and $records[$bottom+3]{"type"} eq "wm8199_setup_dac" and $records[$bottom+2]{"type"} eq "wm8199_setup_unknown_rgb" and $records[$bottom+1]{"type"} eq "wm8199_setup_pga" and $records[$bottom]{"type"} eq "wm8199_register_write" and $records[$bottom]{"register"} eq "SetupReg6" and $records[$bottom]{"data"} == 0x06 ) { $current{"type"} = "wm8199_setup_long"; $current{"timestamp"} = $records[$bottom+9]{"timestamp"}; $current{"number"} = $records[$bottom+9]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"buffer"} = std_text("Setup WM8199 long sequence including Offset DACs and PGA Gains."); create_subrecord(10); } elsif($comment_level>3) { return process_lide60_idle(); } else { return(0); } return 1; } ################################################################# ####### CREATE RECORDS CORRESPONDING TO GL841 FIELDS ############ ################################################################# # Analyse at the level of GL841 registers and register fields # Group the GL841 functions together to form records corresponding # to reading and writing fields within the GL841. # For example: set DRAM size, set exposure time, read motor # status and so on. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a sequence of records was recognised and grouped into a subrecord. # 0 otherwise. # sub process_gl841_registers { if(bit_field_register_read_write(0x01, "scanner_setup", "0x%02x", "scanner setup", [0, 1, "scan process : ", "scan", ("disable", "enable")], [1, 1, "shading area : ", "shdarea", ("whole line", "enable")], [2, 1, "DRAM size : ", "dramsel" , ("4Mx1", "4Mx2")], [3, 1, "DRAM size : ", "m16dram", ("4M or 8M", "16M")], [5, 1, "shading : ", "dvdset", ("disable", "enable")], [6, 1, "watchdog timer (register 0x1e): ", "dogend", ("disable", "enable")], [7, 1, "scanner type : ", "cisset", ("CCD", "CIS")] ) ) { } elsif(bit_field_register_read_write(0x02, "motor_setup", "0x%02x", "motor behaviour setup", [0, 1, "deceleration curve : table ", "longcurv", ("4", "5")], [1, 1, "deceleration on home sensor : ", "homeneg", ("rising edge", "falling edge")], [2, 1, "motor direction : ", "mtrrev" , ("forwards", "backwards")], [3, 1, "number of tables to use for accelaration/deceleration: ", "fastfed", ("1", "2")], [4, 1, "motor power and phase : ", "mtrpwr" , ("off", "on")], [5, 1, "auto homing at end of scan : ", "agohome", ("disable", "enable")], [6, 1, "forward/backward movement on buffer full : ", "acdcdis", ("disable", "enable")], [7, 1, "on auto-homing, go to home position : ", "nothome", ("yes", "no")] ) ) { } elsif(bit_field_register_read_write(0x03, "lamp_setup", "0x%02x", "lamp behaviour setup", [0, 4, "lamp on time : ", "lamptim", "%i minutes"], [4, 1, "lamp power : ", "lamppwr", ("off", "on")], [5, 1, "transparency lamp power: ", "xpasel" , ("off", "on")], [6, 1, "dpi function : ", "aveend", ("average", "deletion")], [7, 1, "lamp sleeping mode : ", "lampdog", ("disable", "enable")] ) ) { } elsif(bit_field_register_read_write(0x04, "front_end_setup", "0x%02x", "analog front end setup", [0, 2, "front end operation type : ", "feset" , ("ESIC type 1", "ESIC type 2", "ADI type", "reserved")], [2, 2, "scan colour type : ", "filter" , ("colour", "red", "green", "blue")], [4, 2, "analog front end operation mode: ", "afemod" , ("slow colour pixel-by-pixel", "mono/colour pixel-by-pixel", "fast mono", "undefined")], [6, 1, "bits per channel : ", "bitset" , ("8", "16")], [7, 1, "number of colours : ", "lineart", ("colour/grey", "monochrome")] ) ) { } elsif(bit_field_register_read_write(0x05, "ccd_cis_setup", "0x%02x", "scanner transducer setup", [0, 2, "CCD pixel duration: ", "mtlbase", "%i CCD pixel/system pixel"], [3, 1, "gamma correction : ", "gmmenb" , ("bypass", "enable")], [4, 2, "lamp time out : ", "mtllamp", ('="1*lamptim = ".$gl841_field{"lamptim"}." minutes"', '="2*lamptim = ".2*$gl841_field{"lamptim"}." minutes"', '="4*lamptim = ".4*$gl841_field{"lamptim"}." minutes"', "reserved")], [6, 2, "CCD/CIS resolution: ", "dpihw" , ("600dpi", "1200dpi", "2400dpi", "reserved")] ) ) { } elsif(bit_field_register_read_write(0x06, "pixels_setup", "0x%02x", "pixel clock setup", [0, 3, "gl841 test mode : ", "optest" , ("normal mode (to capture image from AFE)", "set dram bank, power on carriage initiated, ADF (motorto) test", "pixel count patter for ASIC image test", "line count pattern for ASIC image test", "counter and adder test for ASIC simulation", "undefined", "undefined", "undefined" )], [3, 1, "digital sharing gain : ", "gain4" , ("8 times system (4 times is more precise)", "4 times system (4 times is more precise)")], [4, 1, "power on : ", "pwrbit" , ("off", "on")], [5, 3, "scanning mode : ", "scanmod", ("12 clocks/pixel normal mode for scanning (pixel rate colour, fine grey and fine line art)", "12 clocks/pixel bypass mode for calibration (normal mode for pixel rate colour and fine grey)", "reserved", "reserved", "6 clocks/pixel fast mode (line rate colour, fast grey and fast line art)", "15 clocks/pixel for 16 bit colour output", "18 clocks/pixel for 16 bit colour output", "undefined")] ) ) { } elsif(bit_field_register_read_write(0x07, "dma_setup", "0x%02x", "DRAM DMA access setup", [0, 1, "DMA direction under command mode : ", "dmardwr", ("write", "read")], [1, 1, "DMA/MPU access to dram under command mode : ", "dmasel" , ("MPU", "DMA")], [2, 1, "clock per DMA access : ", "fastdma" , ("4 clocks/access (4clocks/16 bits or 4clocks/8 bits)", "2 clocks/access (2clocks/16 bits or 2clocks/8 bits)")], [3, 1, "DMA access type (SRAM/DRAM) : ", "sramsel", ("dram", "sram")] ) ) { } elsif(bit_field_register_read_write(0x08, "gamma_table_setup", "0x%02x", "gamma table setup", [0, 1, "blue channel gamma table address 0x00 is a special value: ", "gmmzb" , ("no", "yes")], [1, 1, "green channel gamma table address 0x00 is a special value: ", "gmmzg" , ("no", "yes")], [2, 1, "red channel gamma table address 0x00 is a special value: ", "gmmzr" , ("no", "yes")], [3, 1, "blue channel gamma table address 0xff is a special value: ", "gmmffb" , ("no", "yes")], [4, 1, "green channel gamma table address 0xff is a special value: ", "gmmffg" , ("no", "yes")], [5, 1, "red channel gamma table address 0xff is a special value: ", "gmmffr" , ("no", "yes")], [6, 1, "gamma table type : ", "decflag", ("increment", "decrement")] ) ) { } elsif(bit_field_register_read_write(0x09, "misc_setup", "0x%02x", "clocks and interface setup", [0, 1, "nWait delay : ", "nwait" , ("no delay", "one clock")], [1, 1, "short CCD SH(TG) period for film scanning: ", "shorttg" , ("disable", "enable")], [2, 1, "EPP interface speed for USB2.0 : ", "enhance" , ("normal", "enhanced")], [3, 1, "scan direction enable : ", "backscan" , ("forwards", "backwards")], [4, 2, "system clock frequency : ", "clkset" , ("24MHz", "30MHz", "40MHz", "48MHz")], [6, 2, "unit of motor table counter : ", "mcntset", ("pixel count", "system clock * 2", "system clock * 3", "system clock * 4")] ) ) { } elsif(bit_field_register_read_write(0x0a, "ram_setup", "0x%02x", "image buffer memory setup", [0, 1, "image buffer external memory type: ", "srambuf", ("dram", "sram")] ) ) { # 0x0b and 0x0c have no function } elsif(bit_field_register_read_write(0x0d, "clear_line_count", "0x%02x", "byte to clear line count", [0, 1, "Set count of lines scanned to zero.", "clrlncnt", "" ] ) ) { } elsif( multi_byte_register_read_write(0x0e, 0x0e, 0xff, "scanreset", "0x%02x", "byte to reset the scanner (excluding RAM contents)") ){ } elsif( multi_byte_register_read_write(0x0f, 0x0f, 0xff, "move", "0x%02x", "command to start the motor moving") ){ } elsif( multi_byte_register_read_write(0x10, 0x11, 0xff, "expr", "0x%04x", "exposure time for red channel (%i)") ){ } elsif( multi_byte_register_read_write(0x12, 0x13, 0xff, "expg", "0x%04x", "exposure time for green channel (%i)") ){ } elsif( multi_byte_register_read_write(0x14, 0x15, 0xff, "expb", "0x%04x", "exposure time for blue channel (%i)") ){ } elsif(bit_field_register_read_write(0x16, "tg_clock_setup", "0x%02x", "TG clock setup", [0, 1, "CP and RS signals when the CCD is in the TG position : ", "ctrldis", ("enable", "disable")], [1, 1, "clock 1/2 signals when the CCD is in the TG position : ", "ckdis" , ("enable", "disable")], [2, 1, "CCD CP and RS polarity : ", "ctrlinv", ("normal", "reverse")], [3, 1, "clock 2 polarity : ", "ck2inv" , ("normal", "reverse")], [4, 1, "clock 1 polarity : ", "ck1inv" , ("normal", "reverse")], [5, 1, "tg polarity : ", "tginv" , ("normal", "reverse")], [6, 1, "image sensor type : ", "toshiba", ("not a toshiba CIS", "toshiba CIS")], [7, 1, "CCD CP and RS state when TG is high : ", "ctrlhi", ("low", "high")] ) ) { } elsif(bit_field_register_read_write(0x17, "tg_width", "0x%02x", "CCD TG width and mode", [0, 6, "CCD TG width: ", "tgw" , "%i"], [6, 2, "CCD TG mode : ", "tgmode", ("without dummy line CCD TG type", "with reflectional document scanning type", "with transparency scanning type", "TGMODE=2 for simulation" )] ) ) { } elsif(bit_field_register_read_write(0x18, "clock_setup", "0x%02x", "clock behaviour", [0, 2, "CCD clock speed multiple for image capture : ", "cksel" , '=$field+1'], [2, 2, "System clocks to delay by for CCD clock 1/2: ", "ckdeley" , "%i"], [4, 1, "Cycles per pixel for CCD clock 1/2 : ", "cktoggle", ("half", "one")], [5, 2, "CCD clock speed multiple for dummy line : ", "dcksel" , '=$field+1'], [7, 1, "clock/TG style : ", "cnset" , ("non-Canon CIS", "Canon CIS")] ) ) { } elsif( multi_byte_register_read_write(0x19, 0x19, 0xff, "expdmy", "0x%02x", "exposure time for dummy line (%i *256*pixel_time)") ){ } elsif(bit_field_register_read_write(0x1a, "clock_setup_2", "0x%02x", "clock setup", [1, 1, "CCD clamping : ", "lineclp", ("pixel", "line")], [2, 1, "clock 3 polarity : ", "ck3inv" , ("normal", "reverse")], [3, 1, "clock 4 polarity : ", "ck4inv" , ("normal", "reverse")], [4, 1, "CCD clocks 1 and 2 output: ", "manual1", ("automatic", "manual")], [5, 1, "CCD clocks 3 and 4 output: ", "manual3", ("automatic", "manual")] ) ) { # 0x1b has no function } elsif(bit_field_register_read_write(0x1c, "ccd_clk1", "0x%02x", "clock speeds and toggle settings", [0, 3, "CCD Line Period : ", "tgtime" , '=(1<<$field)."*lperiod (= ".(1<<$field)*$gl841_field{"lperiod"}.")"'], [3, 1, "Watch dog timeout: ", "mtlwd" , ("single","double")], [4, 1, "CCD clock speed : ", "ckarea" , ("doesn't depend on scan area","depends on scan area")], [5, 1, "CCD clock 1 : ", "ck1mtgl", ("don't use toggle function","use toggle function")], [6, 1, "CCD clock 3 : ", "ck3mtgl", ("don't use toggle function","use toggle function")], [7, 1, "CCD clock 4 : ", "ck4mtgl", ("don't use toggle function","use toggle function")] ) ) { } elsif(bit_field_register_read_write(0x1d, "tg_shoulder", "0x%02x", "CCD TG shoulder width and clock polarities", [0, 5, "CCD TG shoulder width: ", "tgshld", "%i"], [7, 1, "CCD CLKs 2 and 1 : ", "ck1low", ("high", "low")], [7, 1, "CCD CLKs 3 : ", "ck3low", ("high", "low")], [7, 1, "CCD CLKs 4 : ", "ck4low", ("high", "low")] ) ) { } elsif(bit_field_register_read_write(0x1e, "watchdog", "0x%02x", "watchdog timer time and CIS vertical DPI", [0, 4, "CIS vertical dpi or CCD dummy lines: ", "linesel", '=($field==0)?("cis: full dpi, ccd: no dummy line"):("cis: 1/".$field." dpi, ccd: ".$field." dummy line(s)")'], [4, 4, "Watchdog time : ", "wdtime" , '=(30*$field)."seconds"'] ) ) { } elsif( multi_byte_register_read_write(0x1f, 0x1f, 0xff, "scanfed", "%i", "move to scanning position using table one under two table operation (%i)") ){ } elsif( multi_byte_register_read_write(0x20, 0x20, 0xff, "bufsel" , "%i", '="buffer forward/backward movement condition (".($field*4096)." words)"') ){ } elsif( multi_byte_register_read_write(0x21, 0x21, 0xff, "stepno" , "%i", "length of acceleration table 1 (forward accel/decel) to %i") ){ } elsif( multi_byte_register_read_write(0x22, 0x22, 0xff, "fwdstep", "%i", "number of forward steps for scan head (%i)") ){ } elsif( multi_byte_register_read_write(0x23, 0x23, 0xff, "bwdstep", "%i", "number of backward steps for scan head (%i)") ){ } elsif( multi_byte_register_read_write(0x24, 0x24, 0xff, "fastno" , "%i", "length of acceleration table 2 (backward accel/decel) to %i") ){ } elsif( multi_byte_register_read_write(0x25, 0x27, 0x0f, "lincnt", "%7i", "the scan lines number (%i)") ){ } elsif( multi_byte_register_read_write(0x28, 0x28, 0xff, "gmmwrdata", "0x%02x", "a byte (%i) to the data port of the gamma table") ){ } elsif( multi_byte_register_read_write(0x29, 0x29, 0xff, "lamppwm", "0x%02x", '=sprintf "PWM duty cycle (%i/256=%.3f) to set lamp power", ($field+1), ($field+1)/256') ){ } elsif( multi_byte_register_read_write(0x2a, 0x2b, 0xff, "ramaddr", "0x%04x", "start address for image RAM access (0x%04x0)") ){ } elsif( multi_byte_register_read_write(0x2c, 0x2d, 0x0f, "dpiset", "0x%03x", "resolution (%i dpi)") ){ } elsif( multi_byte_register_read_write(0x2e, 0x2e, 0xff, "bwhi", "%i", "black and white high threshold (%i)") ){ } elsif( multi_byte_register_read_write(0x2f, 0x2f, 0xff, "bwlow", "%i", "black and white low threshold (%i)") ){ } elsif( multi_byte_register_read_write(0x30, 0x31, 0xff, "strpixel", "0x%04x", '=sprintf "the beginning pixel position (%i pixels)",$field-($gl841_field{"tgw"}+2*$gl841_field{"tgshld"})') ){ } elsif( multi_byte_register_read_write(0x32, 0x33, 0xff, "endpixel", "0x%04x", '=sprintf "the end pixel position (%i pixels)",$field-($gl841_field{"tgw"}+2*$gl841_field{"tgshld"})') ){ } elsif( multi_byte_register_read_write(0x34, 0x34, 0xff, "dummy", "0x%02x", '=sprintf "the dummy/optical_black pixel position (%i pixels)",$field-($gl841_field{"tgw"}+2*$gl841_field{"tgshld"})') ){ } elsif( multi_byte_register_read_write(0x35, 0x37, 0x0f, "maxwd", "%7i", " Write the number of words per scanline (%i), the scanner will pause scanning if the image buffer has less than this amount of free space,") ){ } elsif( multi_byte_register_read_write(0x38, 0x39, 0xff, "lperiod", "0x%04x", "line period (%i pixels)") ){ } elsif( multi_byte_register_read_write(0x3a, 0x3b, 0x01, "fewrdata", "0x%03x", "a word (%i) to the control registers of the WM8199 analog frontend") ){ } elsif( multi_byte_register_read_write(0x3c, 0x3c, 0xff, "ramwrdata", "0x%02x", "a byte (%i) to the data port of the image buffer") ){ } elsif( multi_byte_register_read_write(0x3d, 0x3f, 0x0f, "feedl", "%7i", "the number of lines to feed during motor move (%i)") ){ } elsif(bit_field_register_read_write(0x40, "motor_status", "0x%02x", "motor status", [0, 1, "scanner mode: ", "dataenb", ("scanning", "command")], [1, 1, "motor is : ", "motmflg", ("stopped", "moving")], [2, 1, "motor speed : ", "hispdflg" , ("normal", "high")] ) ) { } elsif(bit_field_register_read_write(0x41, "scanner_status", "0x%02x", "scanner status", [0, 1, "motor is : ", "motorenb", ("not processing", "processing")], [1, 1, "front end is : ", "febusy" , ("not busy (ready to read/write)", "busy (not ready to read/write)")], [2, 1, "lamp is : ", "lampsts" , ("off", "on")], [3, 1, "scanner is position: ", "homesnr" , ("not home", "home")], [4, 1, "scanning is : ", "scanfsh" , ("not finished", "finished")], [5, 1, "motor feeding : ", "feedfsh" , ("not finished", "finished")], [6, 1, "image buffer is : ", "bufempty", ("not empty", "empty")], [7, 1, "power : ", "pwrbit" , ("off", "on")] ) ) { } elsif( multi_byte_register_read_write(0x42, 0x44, 0x0f, "validword", "%7i", "number of bytes available to read from the image buffer (%i)") ){ } elsif( multi_byte_register_read_write(0x45, 0x45, 0xff, "ramrddata", "0x%02x", "a byte (%i) from the data port of the image buffer") ){ } elsif( multi_byte_register_read_write(0x46, 0x47, 0x01, "ferddata", "0x%03x", "a word (%i) from the control registers of the WM8199 analog frontend") ){ } elsif( multi_byte_register_read_write(0x48, 0x4a, 0x0f, "fedcnt", "%7i", "the number of motor feeding steps executed") ){ } elsif( multi_byte_register_read_write(0x4b, 0x4d, 0x0f, "scancnt", "%7i", "the number of lines scanned") ){ } elsif( multi_byte_register_read_write(0x4e, 0x4e, 0xff, "gmmrddata", "0x%02x", "a byte (%i) from the data port of the gamma table") ){ # 0x4f has no function } elsif( multi_byte_register_read_write(0x50, 0x50, 0x3f, "ferda", "0x%02x", "an address pointer (%i) for the control registers of the WM8199 analog frontend") ){ } elsif( multi_byte_register_read_write(0x51, 0x51, 0x3f, "fewra", "0x%02x", "an address pointer (%i) for the control registers of the WM8199 analog frontend") ){ } elsif( multi_byte_register_read_write(0x52, 0x52, 0x1f, "rhi" , "0x%02x", "setup to read the MSB of the red channel R[15:8] on phase %i of the ADC sequence") ){ } elsif( multi_byte_register_read_write(0x53, 0x53, 0x1f, "rlow", "0x%02x", "setup to read the LSB of the red channel R[7:0] on phase %i of the ADC sequence") ){ } elsif( multi_byte_register_read_write(0x54, 0x54, 0x1f, "ghi" , "0x%02x", "setup to read the MSB of the green channel G[15:8] on phase %i of the ADC sequence") ){ } elsif( multi_byte_register_read_write(0x55, 0x55, 0x1f, "glow", "0x%02x", "setup to read the LSB of the green channel G[7:0] on phase %i of the ADC sequence") ){ } elsif( multi_byte_register_read_write(0x56, 0x56, 0x1f, "bhi" , "0x%02x", "setup to read the MSB of the blue channel B[15:8] on phase %i of the ADC sequence") ){ } elsif( multi_byte_register_read_write(0x57, 0x57, 0x1f, "blow", "0x%02x", "setup to read the LSB of the blue channel B[7:0] on phase %i of the ADC sequence") ){ } elsif(bit_field_register_read_write(0x58, "vsmp_setup", "0x%02x", "setup of image sampling", [0, 3, "pulse width: ", "vsmpw", "%i"], [3, 5, "phase : ", "vsmp" , '="%i (".(sprintf "%i phase/pixel",$current{"field_vsmp"}+$current{"field_vsmpw"}).")"'] ) ) { } elsif(bit_field_register_read_write(0x59, "bsmp_setup", "0x%02x", "setup of dark voltage sampling", [0, 3, "pulse width: ", "bsmpw", "%i"], [3, 5, "phase : ", "bsmp" , '="%i (".(sprintf "%i phase/pixel",$current{"field_bsmp"}+$current{"field_bsmpw"}).")"'] ) ) { } elsif(bit_field_register_read_write(0x5a, "adc_clocks", "0x%02x", "setup of ADC clocks", [0, 4, "front-end reset level clamp for line rate scanning: ", "rlc" , "%i"], [4, 2, "front-end CDSREF for line rate scanning : ", "cdsref" , "%i"], [6, 1, "reset level clamp on a pixel by pixel basis : ", "rlcsel" , ("don't select", "select")], [7, 1, "ADC clocm polarity : ", "adclkinv", ("normal", "reverse")] ) ) { } elsif( multi_byte_register_read_write(0x5b, 0x5c, 0x03, "gmmaddr", "0x%03x", "start address for gamma table RAM access (0x%03x0)") ){ } elsif( multi_byte_register_read_write(0x5d, 0x5d, 0xff, "hispd", "%i", "change of the moving speed during moving (ENGLISH??) (%i)") ){ } elsif(bit_field_register_read_write(0x5e, "accel_decel", "0x%02x", "setup of acceleration/deceleration", [0, 5, "acceleration/deceleration stop time : ", "stoptim", "%i"], [5, 3, "deceleration step whenever going home: ", "decsel" , '=(1<<$field)." steps"'] ) ) { } elsif( multi_byte_register_read_write(0x5f, 0x5f, 0xff, "fmovdec", "%i", "length of acceleration table 5 (go home fast deceleration) to %i") ){ } elsif( multi_byte_register_read_write(0x60, 0x62, 0x0f, "z1mod", "%7i", "slope curve table mode under buffer full moving (%i)") ){ } elsif( multi_byte_register_read_write(0x63, 0x65, 0x0f, "z2mod", "%7i", "slope curve table mode moving to start of scan (%i)") ){ } elsif( multi_byte_register_read_write(0x66, 0x66, 0xff, "phfreq", "0x%02x", '=sprintf "PWM frequency for uni-polar motor phase of %fMHz", 24/(($field+1)*4)') ){ } elsif(bit_field_register_read_write(0x67, "stepmov", "0x%02x", "step and duty cycle for scanning movement (table 1)", [0, 6, "PWM duty cycle for uni-polar motor phase: ", "mtrpwm" , '=sprintf "%i/64 (=%.2f)",$field+1,($field+1)/64'], [6, 2, "Step selection : ", "stepsel" , ("full step (bi-polar) / two phase on full step (uni-polar)", "half step (bi-polar) / half step (uni-polar)", "quarter step (bi-polar) / reserved (uni-polar)", "reserved (bi-polar) / single phase on full step (uni-polar)" )] ) ) { } elsif(bit_field_register_read_write(0x68, "fastmov", "0x%02x", "step and duty cycle for fast movement (table 2)", [0, 6, "PWM duty cycle for uni-polar motor phase: ", "fastpwm" , '=sprintf "%i/64 (=%.2f)",$field+1,($field+1)/64'], [6, 2, "Step selection : ", "fstpsel" , ("full step (bi-polar) / two phase on full step (uni-polar)", "half step (bi-polar) / half step (uni-polar)", "quarter step (bi-polar) / reserved (uni-polar)", "reserved (bi-polar) / single phase on full step (uni-polar)" )] ) ) { } elsif( multi_byte_register_read_write(0x69, 0x69, 0xff, "fshdec" , "%i", "length of acceleration table 3 (scan finish deceleration) to %i") ){ } elsif( multi_byte_register_read_write(0x6a, 0x6a, 0xff, "fmovno" , "%i", "length of acceleration table 4 (fast moving accel/deccl) to %i") ){ } elsif(bit_field_register_read_write(0x6b, "motor_gpio17_18", "0x%02x", "Set motor pins and GPIO17-18", [0, 1, "GPO17 : ", "gpo17" , "%i"], [1, 1, "GPO18 : ", "gpo18" , "%i"], [4, 1, "GPOM11 : ", "gpom11" , ("select as GPIO11", "select as bipolar motor driver Vref input voltage")], [5, 1, "GPOM12 : ", "gpom12" , ("select as GPIO12", "select as bipolar motor driver Vref input voltage")], [6, 1, "GPOM13 : ", "gpom13" , ("select as GPIO13", "select as MOTORTGO output")], [7, 1, "Multifilm: ", "multifilm", ("motor phase normal", "control motor phase idle to meet multi-film scan")] ) ) { } elsif(bit_field_register_read_write(0x6c, "gpio16_9_data", "0x%02x", "state of GPIO pins 16 to 9", [0, 1, "gpio9 : ", "gpio9" , "%i"], [1, 1, "gpio10: ", "gpio10", "%i"], [2, 1, "gpio11: ", "gpio11", "%i"], [3, 1, "gpio12: ", "gpio12", "%i"], [4, 1, "gpio13: ", "gpio13", "%i"], [5, 1, "gpio14: ", "gpio14", "%i"], [6, 1, "gpio15: ", "gpio15", "%i"], [7, 1, "gpio16: ", "gpio16", "%i"] ) ) { } elsif(bit_field_register_read_write(0x6d, "gpio8_1_data", "0x%02x", "state of GPIO pins 8 to 1", [0, 1, "gpio9 : ", "gpio9" , "%i"], [1, 1, "gpio10: ", "gpio10", "%i"], [2, 1, "gpio11: ", "gpio11", "%i"], [3, 1, "gpio12: ", "gpio12", "%i"], [4, 1, "gpio13: ", "gpio13", "%i"], [5, 1, "gpio14: ", "gpio14", "%i"], [6, 1, "gpio15: ", "gpio15", "%i"], [7, 1, "gpio16: ", "gpio16", "%i"] ) ) { } elsif(bit_field_register_read_write(0x6e, "gpio16_9_dir", "0x%02x", "direction of GPIO pins 16 to 9", [0, 1, "gpio9 : ", "gpioe9" , ("in", "out")], [1, 1, "gpio10: ", "gpioe10", ("in", "out")], [2, 1, "gpio11: ", "gpioe11", ("in", "out")], [3, 1, "gpio12: ", "gpioe12", ("in", "out")], [4, 1, "gpio13: ", "gpioe13", ("in", "out")], [5, 1, "gpio14: ", "gpioe14", ("in", "out")], [6, 1, "gpio15: ", "gpioe15", ("in", "out")], [7, 1, "gpio16: ", "gpioe16", ("in", "out")] ) ) { } elsif(bit_field_register_read_write(0x6f, "gpio8_1_dir", "0x%02x", "direction of GPIO pins 8 to 1", [0, 1, "gpio1 : ", "gpioe1", ("in", "out")], [1, 1, "gpio2 : ", "gpioe2", ("in", "out")], [2, 1, "gpio3 : ", "gpioe3", ("in", "out")], [3, 1, "gpio4 : ", "gpioe4", ("in", "out")], [4, 1, "gpio5 : ", "gpioe5", ("in", "out")], [5, 1, "gpio6 : ", "gpioe6", ("in", "out")], [6, 1, "gpio7 : ", "gpioe7", ("in", "out")], [7, 1, "gpio8 : ", "gpioe8", ("in", "out")] ) ) { } elsif( multi_byte_register_read_write(0x70, 0x70, 0x1f, "rsh", "0x%02x", "CCD RS rising edge position (%i)") ){ } elsif( multi_byte_register_read_write(0x71, 0x71, 0x1f, "rsl", "0x%02x", "CCD RS falling edge position (%i)") ){ } elsif( multi_byte_register_read_write(0x72, 0x72, 0x1f, "cph", "0x%02x", "CP rising edge position (%i)") ){ } elsif( multi_byte_register_read_write(0x73, 0x73, 0x1f, "cpl", "0x%02x", "CP falling edge position (%i)") ){ } elsif( multi_byte_register_read_write(0x74, 0x76, 0x03, "ck1map", "0x%05x", "CCD clock 1 mapping bits (0x%05x)") ){ } elsif( multi_byte_register_read_write(0x77, 0x79, 0x03, "ck3map", "0x%05x", "CCD clock 3 mapping bits (0x%05x)") ){ } elsif( multi_byte_register_read_write(0x7a, 0x7c, 0x03, "ck4map", "0x%05x", "CCD clock 4 mapping bits (0x%05x)") ){ } elsif(bit_field_register_read_write(0x7d, "clock_pos", "0x%02x", "setup for position of clocks relative to system clock", [0, 1, "delay vsmp and bsmp outputs : ", "dlyset", ("no delay", "8.33ns")], [1, 1, "position AFE dark sample pulse (vsmp) on the following edge of the system clock : ", "vsmpneg", ("positive", "negative")], [2, 1, "position AFE video sample pulse (bsmp) on the following edge of the system clock: ", "bsmpneg", ("positive", "negative")], [3, 1, "position CCD CP pulse on the following edge of the system clock : ", "cpneg", ("positive", "negative")], [4, 1, "position CCD RS pulse on the following edge of the system clock : ", "rsneg", ("positive", "negative")], [5, 1, "position clock 4 pulse on the following edge of the system clock : ", "ck4neg", ("positive", "negative")], [6, 1, "position clock 3 pulse on the following edge of the system clock : ", "ck3neg", ("positive", "negative")], [7, 1, "position clocks 1 and 2 pulse (vsmp) on the following edge of the system clock : ", "ck1neg", ("positive", "negative")] ) ) { } elsif(bit_field_register_read_write(0x7e, "gpio13_18_led_control", "0x%02x", "setup for GPIO pins 13 to 18 for normal or LED (blinking) operation", [0, 1, "gpio13: ", "goled13", ("Normal", "Blinking (LED)")], [1, 1, "gpio14: ", "goled14", ("Normal", "Blinking (LED)")], [2, 1, "gpio15: ", "goled15", ("Normal", "Blinking (LED)")], [3, 1, "gpio16: ", "goled16", ("Normal", "Blinking (LED)")], [4, 1, "gpio17: ", "goled17", ("Normal", "Blinking (LED)")], [5, 1, "gpio18: ", "goled18", ("Normal", "Blinking (LED)")] ) ) { } elsif(bit_field_register_read_write(0x7f, "delays", "0x%02x", "setup delays", [0, 4, "led blinking speed: ", "ledcnt", '=($field*100)." ms"'], [4, 2, "vsmp output delay : ", "vsmpdly", ("no delay", "8.33ns", "16.67ns", "25ns")], [6, 2, "bsmp output delay : ", "bsmpdly", ("no delay", "8.33ns", "16.67ns", "25ns")] ) ) { } elsif(bit_field_register_read_write(0x80, "vref", "0x%02x", "setup reference voltages of the motor driver IC", [0, 2, "go home moving : ", "vrhome", "%i"], [2, 2, "fast forward moving: ", "vrmove", "%i"], [4, 2, "backward moving : ", "vrback", "%i"], [6, 2, "scan forward moving: ", "vrscan", "%i"]) ) { } elsif( multi_byte_register_read_write(0x81, 0x82, 0x01, "roffset", "0x%03x", "offset for the red channel (%i)") ){ } elsif( multi_byte_register_read_write(0x83, 0x84, 0x01, "goffset", "0x%03x", "offset for the green channel (%i)") ){ } elsif( multi_byte_register_read_write(0x85, 0x86, 0x01, "boffset", "0x%03x", "offset for the blue channel (%i)") ){ } elsif(bit_field_register_read_write(0x87, "led_array", "0x%02x", "various bits", [0, 1, "Automatic channel offset configuration or RLC/ACYC pulsing for CIS colour scan : ", "autoconf", ("disable", "enable")], [1, 1, "select ADCCLK output by : ", "ck4adc" , ("default", "CK4MAP setting")], [2, 1, "CIS LED grey mode : ", "ledadd" , ("normal grey (controlling CIS single colour LED array)", "true grey (controlling CIS RGB LED array)")], [3, 1, "automatic offset configuration for CIS scanning : ", "enoffset", ("disable", "enable")], [4, 1, "generate RLC/ACYC pulse to trigger WM8199 auto-cycling for line-by-line colour scanning : ", "acycnrlc", ("disable", "enable")], [5, 1, "output PH_Y status : ", "ybit" , ("disable", "enable")], [6, 1, "PH_Y output of the YBIT : ", "yenb" , ("disable", "enable")] ) ) { } elsif( # combination $records[$bottom+1]{"type"} eq "gl841_fwdstep_write" and $records[$bottom]{"type"} eq "gl841_bwdstep_write" ) { $current{"type"} = "gl841_num_steps_write"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"f_data"} = $records[$bottom+1]{"data"}; $current{"b_data"} = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf " Setup number of forward steps (%i) and backward steps (%i) for scan head in the GL841.", $current{"f_data"}, $current{"b_data"}); create_subrecord(2); } elsif( # combination $records[$bottom+1]{"type"} eq "gl841_stepno_write" and $records[$bottom]{"type"} eq "gl841_fastno_write" ) { $current{"type"} = "gl841_num_acceleration_steps_write"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"f_data"} = $records[$bottom+1]{"data"}; $current{"b_data"} = $records[$bottom]{"data"}; $current{"buffer"} = std_text(sprintf " Set length of table 1 (forward accel/decel) to %i and length of table 2 (backward accel/decel) to %i in the GL841.", $current{"f_data"}, $current{"b_data"}); create_subrecord(2); } elsif( # combination $records[$bottom+3]{"type"} eq "gl841_stepno_write" and $records[$bottom+2]{"type"} eq "gl841_stepno_read" and $records[$bottom+1]{"type"} eq "gl841_fastno_write" and $records[$bottom]{"type"} eq "gl841_fastno_read" ) { $current{"type"} = "gl841_num_acceleration_steps_write"; $current{"timestamp"} = $records[$bottom+3]{"timestamp"}; $current{"number"} = $records[$bottom+3]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"f_data"} = $records[$bottom+3]{"data"}; $current{"b_data"} = $records[$bottom+1]{"data"}; if(($records[$bottom]{"data"}==$records[$bottom+1]{"data"}) and ($records[$bottom+2]{"data"}==$records[$bottom+3]{"data"})) { $current{"buffer"} = std_text(sprintf " Set length of table 1 (forward accel/decel) to %i and length of table 2 (backward accel/decel) to %i in the GL841 with verification.", $current{"f_data"}, $current{"b_data"}); } else { $current{"buffer"} = std_text("READBACK FAILED ON SETTING ACCEL/DECEL STEPS"); } create_subrecord(4); } elsif( # combination $records[$bottom+2]{"type"} eq "gl841_stepno_write" and $records[$bottom+1]{"type"} eq "gl841_num_steps_write" and $records[$bottom]{"type"} eq "gl841_fastno_write" ) { $current{"type"} = "gl841_forwards_backwards_write"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"f_data"} = $records[$bottom+2]{"data"}; $current{"b_data"} = $records[$bottom]{"data"}; my $temp_buffer = sprintf " Setup motor movement profile."; $temp_buffer .= sprintf "\n forward steps: %i", $records[$bottom+1]{"f_data"}; $temp_buffer .= sprintf "\n backward steps: %i", $records[$bottom+1]{"b_data"}; $temp_buffer .= sprintf "\n length of table 1 (forward accel/decel): %i", $records[$bottom+2]{"data"}; $temp_buffer .= sprintf "\n length of table 2 (backward accel/decel): %i", $records[$bottom]{"data"}; $current{"buffer"} = std_text($temp_buffer); create_subrecord(3); } elsif( # Combination $records[$bottom+2]{"type"} eq "gl841_motor_status_read" and $records[$bottom+1]{"type"} eq "gl841_motor_setup_read" and $records[$bottom]{"type"} eq "gl841_scanner_status_read" ) { $current{"type"} = "gl841_motor_state_read"; $current{"timestamp"} = $records[$bottom+2]{"timestamp"}; $current{"number"} = $records[$bottom+2]{"number"}; $current{"last_number"} = $records[$bottom]{"last_number"}; $current{"data"} = $records[$bottom]{"data"}; $current{"data02"} = $records[$bottom+1]{"data"}; $current{"data40"} = $records[$bottom+2]{"data"}; $current{"buffer"} = std_text(sprintf " Check motor status [0x40]=0x%02x [0x02]=0x%02x [0x41]=0x%02x.", $current{"data40"}, $current{"data02"}, $current{"data"}, ); create_subrecord(3); } elsif( # motor move with fixed data $records[$bottom]{"type"} eq "gl841_move_write" and $records[$bottom]{"data"} == 0xff ) { $current{"type"} = "gl841_motor_move_write"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"buffer"} = std_text(sprintf " Start motor movement."); create_subrecord(1); } elsif($comment_level>2) { return process_wm8199(); } else { return(0); } return 1; } ################################################################# ####### CREATE RECORDS CORRESPONDING TO GL841 OPERATIONS ######## ################################################################# # Group the URBs together to form records corresponding to operations # on the GL841. For example: read a register, write a register, # do a bulk write and so on. # Convert an integer representing a USB version # number into a string. # Inputs: # $release : The version number (an integer) # # Returns: A string describing the version number (major release, # minor release, and subversion). # sub descriptor_release { my $release = shift(@_); my $sub_ver = $release%16; $release /= 16; my $minor_ver = $release%16; $release /= 16; my $major_ver1 = $release%16; $release /= 16; my $major_ver2 = $release%16; return sprintf("%i.$minor_ver.$sub_ver", 10 * $major_ver2 + $major_ver1); } # Handle descritor classes. Not yet written. # sub descriptor_class { } # Interpret a field of a USB descriptor # and return a string describing it. # Inputs: # $field : The name of the USB descritpor field (a string) # $value : The value of the USB descritpor field (an integer) # $modifer: An integer which allows the meaning of the field # to be modified. Some fields have a slightly # different meaning depending on which type # of descriptor they appear in. # # Returns: A string giving the value of the descriptor # field and describing its meaning. # sub descriptor_field { my $field = shift(@_); my $value = shift(@_); my $modifier = shift(@_); my $descriptor_field_value; my $descriptor_field_comment; my $descriptor_field_name; if($field eq "Release") { $descriptor_field_value = descriptor_release($value); $descriptor_field_comment = "Version of the USB protocol." } elsif($field eq "Class" or $field eq "Iclass") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_class = $value; if($value == 0) { $descriptor_field_comment = "Class specified within each interface." } elsif($value == 0xff) { $descriptor_field_comment = "Class is vendor specific." } else { $descriptor_field_comment = "Class is USB-IF assigned (0x01-0xfe)." } } elsif($field eq "Subclass" or $field eq "Isubclass") { $descriptor_field_value = sprintf("0x%02x", $value); if($descriptor_field_class == 0) { $descriptor_field_comment = "Subclass is zero since Class is zero." } elsif($descriptor_field_class == 0xff) { $descriptor_field_comment = "Subclass is vendor specific." } else { $descriptor_field_comment = "Subclass is USB-IF assigned (since Class is USB-IF assigned)." } } elsif($field eq "Protocol" or $field eq "Iprotocol") { $descriptor_field_value = sprintf("0x%02x", $value); if($value == 0) { $descriptor_field_comment = "Protocol specified within each interface." } elsif($value == 0xff) { $descriptor_field_comment = "Protocol is vendor specific." } else { $descriptor_field_comment = "Protocol is USB-IF assigned (0x01-0xfe)." } } elsif($field eq "Maxpacket") { $descriptor_field_value = sprintf("%i", $value); $descriptor_field_comment = "Maximum Packet Size for endpoint"; if($modifier==1) { $descriptor_field_comment .= "."; } else { $descriptor_field_comment .= "0 (the control endpoint)."; } } elsif($field eq "Vendor") { $descriptor_field_value = sprintf("0x%04x", $value); $descriptor_field_comment = "The product vendor: "; if($value == 0x04a9) { $descriptor_field_comment .= "Canon" } } elsif($field eq "Product") { $descriptor_field_value = sprintf("0x%04x", $value); $descriptor_field_comment = "The product model: "; if($value == 0x221c) { $descriptor_field_comment .= "LiDE 60" } } elsif($field eq "Devrelease") { $descriptor_field_value = descriptor_release($value); $descriptor_field_comment = "Release number of this device." } elsif($field eq "Imanuf") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Index to string describing the manufacturer."; } elsif($field eq "Iproduct") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Index to string describing the product."; } elsif($field eq "Iserial") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Index to string describing the device's serial number."; } elsif($field eq "Numconf") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Number of configurations for this device."; } elsif($field eq "Totalength") { $descriptor_field_value = sprintf("%i", $value); $descriptor_field_comment = "Total bytes returned for this configuration, its interfaces and endpoints."; } elsif($field eq "NumIntf") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Number of interfaces supported by this configuration."; } elsif($field eq "ConfigVal") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Value to use to select this configuration."; } elsif($field eq "Iconfig") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Index to string describing this configuration."; } elsif($field eq "Attrib") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Attributes: "; if($modifier!=1) { if($value&0x20) { $descriptor_field_comment .= "Remote_wakeup "; } if($value&0x40) { $descriptor_field_comment .= "Self_powered "; } } else { $descriptor_field_comment .= "Transfer="; if(($value&0x03) == 0) { $descriptor_field_comment .= "control"; } elsif(($value&0x03) == 1) { $descriptor_field_comment .= "isosynchronous"; } elsif(($value&0x03) == 2) { $descriptor_field_comment .= "bulk"; } elsif(($value&0x03) == 3) { $descriptor_field_comment .= "interrupt"; } $descriptor_field_comment .= " Synch="; $value = $value>>2; if(($value&0x03) == 0) { $descriptor_field_comment .= "no syncronisation"; } elsif(($value&0x03) == 1) { $descriptor_field_comment .= "asyncronous"; } elsif(($value&0x03) == 2) { $descriptor_field_comment .= "adaptive"; } elsif(($value&0x03) == 3) { $descriptor_field_comment .= "synchronous"; } $descriptor_field_comment .= " Usage="; $value = $value>>2; if(($value&0x03) == 0) { $descriptor_field_comment .= "data"; } elsif(($value&0x03) == 1) { $descriptor_field_comment .= "feedback"; } elsif(($value&0x03) == 2) { $descriptor_field_comment .= "implicit feedback data"; } elsif(($value&0x03) == 3) { $descriptor_field_comment .= "reserved"; } } } elsif($field eq "MaxPwr") { $descriptor_field_value = sprintf("%imA", $value*2); $descriptor_field_comment = "Maximum operational power required by this device."; } elsif($field eq "IntfNum") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Number of this interface."; } elsif($field eq "AltSet") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Value used to select alternative setting for this interface."; } elsif($field eq "NumEndp") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Number of end points supported by this interface (excluding control endpoint)."; } elsif($field eq "Iindex") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = "Index to string describing this interface."; } elsif($field eq "EndpAddr") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = sprintf "Endpoint Address: number=%i direction=", ($value&0x0f); if($value&0x80) { $descriptor_field_comment .= "read"; } else { $descriptor_field_comment .= "write"; } } elsif($field eq "Interval") { $descriptor_field_value = sprintf("0x%02x", $value); $descriptor_field_comment = sprintf "Polling interval for this endpoint."; } $descriptor_field_name = $field; return sprintf " %10s = %6s %s", $descriptor_field_name, $descriptor_field_value, $descriptor_field_comment; } # Given an array of bytes (LSB first) representing a # sequence of USB descriptors, decode the first # descriptor found and return its length. A string # will be created describing the descriptor. # Inputs: # $field : An array of bytes (each as a integer, LSB first) # # Globals: # $descriptor_text : A string to which a description # of the descriptor is appended. # # Returns: The length of the descriptor found. # sub process_descriptor { #See table 9.5 for descriptor types my $descriptor_length = $_[0]; my $descriptor_type = $_[1]; if($descriptor_type==1) {#device $descriptor_text .= sprintf "GET DEVICE DESCRIPTOR\n"; $descriptor_text .= descriptor_field("Release" , $_[2] +256*$_[3] )."\n"; $descriptor_text .= descriptor_field("Class" , $_[4] )."\n"; $descriptor_text .= descriptor_field("Subclass" , $_[5] )."\n"; $descriptor_text .= descriptor_field("Protocol" , $_[6] )."\n"; $descriptor_text .= descriptor_field("Maxpacket" , $_[7] )."\n"; $descriptor_text .= descriptor_field("Vendor" , $_[8] +256*$_[9] )."\n"; $descriptor_text .= descriptor_field("Product" , $_[10]+256*$_[11])."\n"; $descriptor_text .= descriptor_field("Devrelease", $_[12]+256*$_[13])."\n"; $descriptor_text .= descriptor_field("Imanuf" , $_[14] )."\n"; $descriptor_text .= descriptor_field("Iproduct" , $_[15] )."\n"; $descriptor_text .= descriptor_field("Iserial" , $_[16] )."\n"; $descriptor_text .= descriptor_field("Numconf" , $_[17] ); } elsif($descriptor_type==2) {#configuration $descriptor_text .= sprintf "GET CONFIG DESCRIPTOR\n"; $descriptor_text .= descriptor_field("Totalength", $_[2]+256*$_[3])."\n"; $descriptor_text .= descriptor_field("NumIntf" , $_[4] )."\n"; $descriptor_text .= descriptor_field("ConfigVal" , $_[5] )."\n"; $descriptor_text .= descriptor_field("Iconfig" , $_[6] )."\n"; $descriptor_text .= descriptor_field("Attrib" , $_[7] )."\n"; $descriptor_text .= descriptor_field("MaxPwr" , $_[8] ); } elsif($descriptor_type==4) {#interface $descriptor_text .= sprintf "GET INTFCE DESCRIPTOR\n"; $descriptor_text .= descriptor_field("IntfNum" , $_[2])."\n"; $descriptor_text .= descriptor_field("AltSet" , $_[3])."\n"; $descriptor_text .= descriptor_field("NumEndp" , $_[4])."\n"; $descriptor_text .= descriptor_field("Iclass" , $_[5])."\n"; $descriptor_text .= descriptor_field("Isubclass" , $_[6])."\n"; $descriptor_text .= descriptor_field("Iprotocol" , $_[7])."\n"; $descriptor_text .= descriptor_field("Iindex" , $_[8]); } elsif($descriptor_type==5) {#endpoint $descriptor_text .= sprintf "GET ENDPNT DESCRIPTOR\n"; $descriptor_text .= descriptor_field("EndpAddr" , $_[2] )."\n"; $descriptor_text .= descriptor_field("Attrib" , $_[3] , 1)."\n"; $descriptor_text .= descriptor_field("Maxpacket" , $_[4]+256*$_[5], 1)."\n"; $descriptor_text .= descriptor_field("Interval" , $_[6] ); } elsif($descriptor_type==6) {#device qualifier $descriptor_text .= sprintf "GET DEVQUAL DESCRIPTOR\n"; $descriptor_text .= descriptor_field("Release" , $_[2] +256*$_[3] )."\n"; $descriptor_text .= descriptor_field("Class" , $_[4] )."\n"; $descriptor_text .= descriptor_field("Subclass" , $_[5] )."\n"; $descriptor_text .= descriptor_field("Protocol" , $_[6] )."\n"; $descriptor_text .= descriptor_field("Maxpacket" , $_[7] )."\n"; $descriptor_text .= descriptor_field("Numconf" , $_[8] ); $descriptor_text .= sprintf " Reserved = 0x%02x", $_[9]; } else { $descriptor_text .= sprintf "UNKNOWN DESCRIPTOR (Length=0x%02x, Type=0%x02x)", $descriptor_length, $descriptor_type; } return $descriptor_length; } # Mark up records in a standard format for printing. # Inputs: # $text : A string containing the text of the record # to be printed. # # Globals: # current : The record to be printed. # # Returns: A string containing the printed record, # nicely formated with a time stamp, URB # number and indentation. # sub std_text { my $text = shift(@_); my $output = sprintf "%5i", $current{"number"}, $current{"number"}; if (exists $current{"last_number"}) { $output .= sprintf "-%5i", $current{"last_number"}, $current{"last_number"}; } else { $output .= sprintf " "; } $output .= sprintf ":%8ims ", $current{"timestamp"}, $current{"timestamp"}; my $pad = " " x length($output); $output .= sprintf "$text"; $output =~ s/\n(.*)/\n$pad$1/g; # pad multiline text $output .= "\n"; return $output; } # Group the URBs together to form records corresponding to operations # on the GL841. For example: read a register, write a register, # do a bulk write and so on. # # Globals: # $records : An array of records. # $bottom : The index of the record being tested. # # Returns: 1 if a sequence of records was recognised and grouped into a subrecord. # 0 otherwise. # sub process_urb { if( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0x40 and $records[$bottom]{"req"} == 0x04 and $records[$bottom]{"req_value"} == 0x83 and $records[$bottom]{"req_index"} == 0x00 and $records[$bottom]{"data_length"} == 2 and $records[$bottom]{"direction"} eq "wrote" ) { $current{"type"} = "gl841_register_write"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"address"} = $records[$bottom]{"data"}[0]; $current{"data"} = $records[$bottom]{"data"}[1]; $current{"buffer"} = std_text(sprintf " 0x%02x -> [0x%02x] Write to register in the GL841", $current{"data"}, $current{"address"}); create_subrecord(1); } elsif ( $records[$bottom+1]{"type"} eq "urb_control" and $records[$bottom+1]{"fn_out"} eq "v" and $records[$bottom+1]{"fn_in"} eq "c" and $records[$bottom+1]{"req_type"} == 0x40 and $records[$bottom+1]{"req"} == 0x0c and $records[$bottom+1]{"req_value"} == 0x83 and $records[$bottom+1]{"req_index"} == 0x00 and $records[$bottom+1]{"data_length"} == 1 and $records[$bottom+1]{"direction"} eq "wrote" and $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0xc0 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x84 and $records[$bottom]{"req_index"} == 0x00 and $records[$bottom]{"data_length"} == 1 and $records[$bottom]{"direction"} eq "read " ) { $current{"type"} = "gl841_register_read"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"last_number"} = $records[$bottom]{"number"}; $current{"address"} = $records[$bottom+1]{"data"}[0]; $current{"data"} = $records[$bottom]{"data"}[0]; $current{"buffer"} = std_text(sprintf "[0x%02x] -> 0x%02x Read from register in the GL841", $current{"address"}, $current{"data"}); create_subrecord(2); } elsif ( $records[$bottom+1]{"type"} eq "urb_control" and $records[$bottom+1]{"fn_out"} eq "v" and $records[$bottom+1]{"fn_in"} eq "c" and $records[$bottom+1]{"req_type"} == 0x40 and $records[$bottom+1]{"req"} == 0x0c and $records[$bottom+1]{"req_value"} == 0x83 and $records[$bottom+1]{"req_index"} == 0x00 and $records[$bottom+1]{"data_length"} == 1 and $records[$bottom+1]{"direction"} eq "wrote" ) { $current{"type"} = "gl841_register_read_address_only"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"address"} = $records[$bottom+1]{"data"}[0]; if($current{"address"}==0x45){ $current{"buffer"} = std_text(sprintf "Point to the read port of the GL841 image buffer DRAM (address 0x%02x) in preparation for a bulk read.", $current{"address"} ); } elsif($current{"address"}==0x4e) { $current{"buffer"} = std_text(sprintf "Point to the read port of the GL841 gamma table DRAM (address 0x%02x) in preparation for a bulk read.", $current{"address"} ); } elsif($current{"address"}==0x3c) { $current{"buffer"} = std_text(sprintf "Point to the write port of the GL841 image buffer DRAM (address 0x%02x) in preparation for a bulk write.", $current{"address"} ); } elsif($current{"address"}==0x28) { $current{"buffer"} = std_text(sprintf "Point to the write port of the GL841 gamma table DRAM (address 0x%02x) in preparation for a bulk write.", $current{"address"} ); } else { $current{"buffer"} = std_text(sprintf "Wrote an address 0x%02x in preparation for a bulk read/write from/to the GL841.", $current{"address"} ); } create_subrecord(1, 1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0xc0 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x8e and $records[$bottom]{"req_index"} == 0x18 and $records[$bottom]{"data_length"} == 1 and $records[$bottom]{"direction"} eq "read " ) { $current{"type"} = "gl841_start_stop_bulk"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"data"} = $records[$bottom]{"data"}[1]; $current{"buffer"} = std_text(sprintf "Start/Stop Bulk Transfer (0x%02x)", $current{"data"}); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_bulk" ) { if($records[$bottom]{"direction"} eq "wrote") { $current{"type"} = "gl841_bulk_write"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"data_length"} = $records[$bottom]{"data_length"}; $current{"buffer"} = std_text(sprintf "BULK WRITE (%s bytes)", $current{"data_length"}); create_subrecord(1); } else { $current{"type"} = "gl841_bulk_read"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"data_length"} = $records[$bottom]{"data_length"}; $current{"buffer"} = std_text(sprintf "BULK READ (%s bytes)", $current{"data_length"}); create_subrecord(1); } } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "d" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0x00 and $records[$bottom]{"req"} == 0x00 and $records[$bottom]{"req_value"} == 0x00 and $records[$bottom]{"req_index"} == 0x00 ) { $current{"type"} = "gl841_descriptor"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; my $descriptor_index = 0; my $data_length = $records[$bottom]{"data_length"}; $descriptor_text = ""; while($descriptor_index < $data_length) { if($descriptor_index != 0) { $descriptor_text .= sprintf "\n"; } $descriptor_index += process_descriptor(@{$records[$bottom]{"data"}}[ $descriptor_index .. $records[$bottom]{"data_length"} ]); } $current{"buffer"} = std_text($descriptor_text); if($data_length != $descriptor_index) { $current{"buffer"} .= sprintf "ERROR: Length mismatch in descriptor (URB length=%i, descriptor length=%i)\n", $data_length, $descriptor_index; } create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "s" and $records[$bottom]{"fn_in"} eq "s" and $records[$bottom]{"req_type"} == 0x00 and $records[$bottom]{"req"} == 0x00 and $records[$bottom]{"req_value"} == 0x00 and $records[$bottom]{"req_index"} == 0x00 ) { $current{"type"} = "gl841_select_configuration"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"buffer"} = std_text("SELECT CONFIGURATION See raw log for details"); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0xc0 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x8e and $records[$bottom]{"req_index"} == 0x00 ) { $current{"type"} = "gl841_read_unknown1"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"buffer"} = std_text("READ SOMETHING??? @{$records[$bottom]{\"data\"}}"); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "d" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0xc0 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x8e and $records[$bottom]{"req_index"} == 0x00 ) { $current{"type"} = "gl841_read_unknown_descriptor"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; process_descriptor(@{$records[$bottom]{"data"}}[ $descriptor_index .. $records[$bottom]{"data_length"} ]); $current{"buffer"} = std_text("READ SOME SORT OF DESCRIPTOR\n".$descriptor_text); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0x40 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x8c and $records[$bottom]{"req_index"} == 0x10 ) { $current{"type"} = "gl841_write_unknown1"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"buffer"} = std_text("WRITE SOMETHING??? (index=0x10) @{$records[$bottom]{\"data\"}}"); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0x40 and $records[$bottom]{"req"} == 0x0c and $records[$bottom]{"req_value"} == 0x8c and $records[$bottom]{"req_index"} == 0x0f ) { $current{"type"} = "gl841_write_unknown2"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"buffer"} = std_text("WRITE SOMETHING??? (index=0x0f) @{$records[$bottom]{\"data\"}}"); create_subrecord(1); } elsif( $records[$bottom]{"type"} eq "urb_control" and $records[$bottom]{"fn_out"} eq "v" and $records[$bottom]{"fn_in"} eq "c" and $records[$bottom]{"req_type"} == 0x40 and $records[$bottom]{"req"} == 0x04 and $records[$bottom]{"req_value"} == 0x82 and $records[$bottom]{"req_index"} == 0x00 and $records[$bottom]{"data_length"} == 8 and $records[$bottom]{"direction"} eq "wrote" ) { $current{"type"} = "gl841_write_unknown2"; $current{"timestamp"} = $records[$bottom]{"timestamp"}; $current{"number"} = $records[$bottom]{"number"}; $current{"data"}= [ @{ $records[$bottom]{"data"} } ]; $current{"buffer"} = std_text("WROTE A BLOCK OF 8 BYTES???"); create_subrecord(1); } elsif( substr($records[$bottom+1]{"type"},0,4) eq "urb_" ) { $current{"type"} = "gl841_unknown"; $current{"timestamp"} = $records[$bottom+1]{"timestamp"}; $current{"number"} = $records[$bottom+1]{"number"}; $current{"buffer"} = std_text("UNKNOWN URB"); create_subrecord(1, 1); } elsif($comment_level>1) { return process_gl841_registers(); } else { return(0); } return 1; } ################################################################# ############### SPLIT INPUT INTO RECORDS ######################## ################################################################# # Create a record for each URB in the USB log. # Make an attempt to decode the requst_type, request, # value and index bytes of a USB URB. These bytes # describe things such as the purpose of the URB, # where it i sdirected and so on. # Inputs: # $request_type : The request type byte of the URB. # $request : The request byte of the URB. # $value : The value byte of the URB. # $index : The index byte of the URB. # # Returns: A string describing the purpose of the URB. # sub process_urb_request_type { my $request_type = shift(@_); my $request = shift(@_); my $value = shift(@_); my $index = shift(@_); my $direction = ($request_type>>7)&0x01; my $type = ($request_type>>5)&0x03; my $recipient = $request_type &0x1f; my $comment = " dir:"; if($direction==0) { $comment .= "write"; } else { $comment .= "read "; } $comment .= " type:"; if($type==0) { $comment .= "std"; } elsif($type==1) { $comment .= "class "; } elsif($type==2) { $comment .= "vendor"; } else { $comment .= "resv "; } $comment .= " recipient:"; if($recipient==0) { $comment .= "device"; } elsif($recipient==1) { $comment .= "interf"; } elsif($recipient==2) { $comment .= "endpnt"; } elsif($recipient==3) { $comment .= "other "; } else { $comment .= "resv "; } $comment .= " req:"; if($type==0) { if($request=>0 and $request<=12) { my @request_code = {"get_status", "clear_feature", "reserved", "set_feature", "reserved", "set_address", "get_descriptor", "set_descriptor", "get_configuration", "set_configuration", "get_interface", "set_interface", "synch_frame" }; $comment .= $request_code[$request]; } else { $comment .= "unknown"; } } else { $comment .= "non-std"; } if($recipient==2) { $comment .= " endpnt:"; my $dir = ($index>>7)&0x01; my $endp = $index &0x0f; $comment .= $endp."("; if($dir==1){ $comment .= "rd"; } else { $comment .= "wr"; } $comment .= ")"; } elsif($recipient==1) { $comment .= " intfc:".($index&0xff); } return $comment; } # Parse each line of a file and assemble the data into # records, one for each URB in the file. Once each # URB has been assembled, process_urb() is repeatedly # called until it fails to match a pattern of URB # records. process_urb() tries to recursively group # URBs to form high level actions by the scanner. # # Inputs: # $line : A string containing the line to be parsed. # # Globals: # $records : An array of records. # sub process_line { my $line = shift(@_); if ($line =~ /(.*)ms URB(.*) (\S)(\S) control (\S\S\S\S) (\S\S\S\S) (\S\S\S\S) (\S\S\S\S) len(.*) (wrote|read ) (((\S\S\S\S)\s?)*)/) { $current{"type"} = "urb_control"; $current{"timestamp"} = $1; $current{"number"} = $2; $current{"fn_out"} = $3; $current{"fn_in"} = $4; $current{"req_type"} = decimal($5); #$byte[1] $current{"req"} = decimal($6); #$byte[2] $current{"req_value"} = decimal($7); #$byte[3] $current{"req_index"} = decimal($8); #$byte[4] $current{"data_length"} = $9; #$urb_length $current{"direction"} = $10; @data = split(/ /, $11); foreach $data(@data) { $data = decimal($data); } $current{"data"} = [@data]; $data_index = 8; $current{"line"} = $line.process_urb_request_type($current{"req_type"}, $current{"req"}, $current{"req_value"}, $current{"req_index"}); } elsif ($line =~ /(.*)ms URB(.*) (\S)(\S) (bulk_in |bulk_out) len(.*) (wrote|read ) (SKIPPED|(((\S\S\S\S)\s?)*))/) { $current{"type"} = "urb_bulk"; $current{"timestamp"} = $1; $current{"number"} = $2; $current{"fn_out"} = $3; $current{"fn_in"} = $4; $current{"data_length"} = $6; $current{"direction"} = $7; @data = split(/ /, $8); if($current{"data_length"}>=1 and $data[0] eq "SKIPPED") { $data_index = $current{"data_length"}; } else { $data_index = @data; foreach $data(@data) { $data = decimal($data); } } $current{"data"} = [@data]; $current{"line"} = $line; } elsif ($line =~ /(.*)ms URB(.*) skip/) { $current{"type"} = "urb_skip"; $current{"timestamp"} = $1; $current{"number"} = $2; $current{"data_length"} = 0; $data_index = 0; $current{"line"} = $line; } elsif ($data_index<$current{"data_length"} and $line =~ / (((\S\S\S\S)\s?)*)/) { @data = split(/ /, $1); foreach $data(@data) { $data = decimal($data); } push @{ $current{"data"} }, @data; $data_index += @data; $current{"line"} = $current{"line"}."\n".$line; } else { $current{"type"} = "urb_unknown"; $current{"data_length"} = 0; $data_index = 0; $current{"line"} = $line; # $outfile_buffer .= sprintf "UNKNOWN: $line\n"; } if($data_index >= $current{"data_length"}) { $current{"buffer"} = $current{"line"}."\n"; #$current{"buffer"} .= sprintf " %8ims " , $current{"timestamp"}; #$current{"buffer"} .= sprintf "URB%6i ", $current{"number"}; #$current{"buffer"} .= sprintf "%s%s " , $current{"fn_out"}, $current{"fn_in"}; #if(substr($current{"type"}, 0, 4) eq "urb_") { # $current{"buffer"} .= sprintf "%s " , substr($current{"type"}, 4); #} else { # $current{"buffer"} .= sprintf "%sXX " , substr($current{"type"}, 0, 4) #} #$current{"buffer"} .= sprintf "0x%02x " , $current{"req_type"}; #$current{"buffer"} .= sprintf "0x%02x " , $current{"req"}; #$current{"buffer"} .= sprintf "0x%02x " , $current{"req_value"}; #$current{"buffer"} .= sprintf "0x%02x " , $current{"req_index"}; #$current{"buffer"} .= sprintf "len%6i ", $current{"data_length"}; #$current{"buffer"} .= sprintf "%s " , $current{"direction"}; #foreach my $element(@{$current{"data"}}) { # $current{"buffer"} .= sprintf "0x%02x " , $element; #} #$current{"buffer"} .= "\n"; create_record(); if($comment_level>=1) { while($bottom>0) { while(process_urb()) { } $bottom--; } while(process_urb()) { } } } } ################################################################# ################## EXECUTION STARTS HERE ######################## ################################################################# # 0 = no comments. Input is mirrored to output # 1 = Input is anotated to the GL841 register level # 2 = Input is anotated to the GL841 register and field level # 3 = Input is anotated to the level of WM8199 registers # >3 = Input is annotated as far as possible. $comment_level = 4; if(@ARGV>=1) { if(substr($ARGV[0], 0, 1)=="-") { print "Usage: lide60_analyse.pl [-h] [output directory] [input file]"; die; } $file_subdirectory = $ARGV[0]; } else { $file_subdirectory = "usblog"; } if(@ARGV>=2) { $STDIN = $ARGV[1]; open STDIN or die"Could not open log file: $!"; } #$LOGFILE = "usbsnoop_2005_10_24_22_05_parsed.log"; #$file_subdirectory = "test"; #$file_subdirectory = "/home/johnd/data1/personal/lide60/test"; #open LOGFILE or die"Could not open log file: $!"; $rd_line = 0; $data_index = 0; $data_length = -1; $bulk_flag = 0; $bottom = 0; foreach $line () { chomp($line); # remove the newline from $line. process_line($line); } create_subrecord();