#!/usr/bin/perl #Generate a .c source that preinitializes a coffee file system #David Kopf July 2009 #For coffee file_header structure of #struct file_header { # coffee_page_t log_page; # uint16_t log_records; # uint16_t log_record_size; # coffee_page_t max_pages; # uint8_t deprecated_eof_hint; # uint8_t flags; # char name[COFFEE_NAME_LENGTH]; # } __attribute__((packed)); goto DEFAULTS; START:$version="1.0 dak"; #Process options for($n=0;$n<=$#ARGV;$n++) { $arg=$ARGV[$n]; if ($arg eq "-p") { $n++;$coffee_page_length=$ARGV[$n]; } elsif ($arg eq "-s") { $n++;$coffee_sector_size=$ARGV[$n]; } elsif ($arg eq "-t") { $n++;$coffee_page_t =$ARGV[$n]; } elsif ($arg eq "-f") { $n++;$coffee_name_length=$ARGV[$n]; } elsif ($arg eq "-S") { $n++;$sectionname=$ARGV[$n]; } elsif ($arg eq "-c") { $complement=1; } elsif ($arg eq "-l") { $linkedlist=1; } elsif ($arg eq "-v") { print "avr-makecoffeedata Version $version\n"; } elsif ($arg eq "-d") { $n++;$directory=$ARGV[$n]; } elsif ($arg eq "-o") { $n++;$outputfile=$ARGV[$n]; } else { DEFAULTS: #Set up defaults $coffee_page_length=256; $coffee_sector_size=256; $coffee_page_t=1; $coffee_name_length=16; $complement=0; $directory=""; $outputfile="httpd-fsdata.c"; $linkedlist=0; $sectionname=".coffeefiles"; if (!$version) {goto START;} print "\n"; print "Usage: avr-makecoffeedata <-d input_directory> <-o output_file>\n"; print " Generates c source file to make a pre-initialized coffee file system\n"; print " The default output file is $outputfile\n"; print " The input directory structure is copied. If input_directory is specified\n"; print " it becomes the root \"/\". If no input_directory is specified the first\n"; print " subdirectory found in the current directory is used as the root.\n"; print " WARNING : If the output file exists it will be overwritten without confirmation\n\n"; print " Options are:\n"; print " -p pagesize Page size in bytes (default $coffee_page_length)\n"; print " -s sectorsize Sector size in bytes (default $coffee_sector_size)\n"; print " -t page_t Number of bytes in coffee_page_t (1,2,or 4, default $coffee_page_t)\n"; print " -f namesize File name field size in bytes (default $coffee_name_length)\n"; print " -S section Section name (default $sectionname)\n"; print " -c Complement the data, useful when flash is all 1s after erase\n"; print " -l Append a linked list of the file starting addresses\n"; print " -v Display the version number\n"; exit; } } #printf "coffee_page_length=$coffee_page_length\n"; #printf "coffee_page_t=$coffee_page_t\n"; #printf "coffee_name_length=$coffee_name_length\n"; #printf "complement=$complement\n"; #printf "directory=$directory\n"; #printf "outputfile=$outputfile\n"; #printf "sectionname=$sectionname; if ($coffee_page_t==1) { $coffeemax=0xff; } elsif ($coffee_page_t==2) { $coffeemax=0xffff; } elsif ($coffee_page_t==4) { $coffeemax=0xffffffff; } else { die "Unsupported coffee_page_t $coffee_page_t\n"; } $coffee_header_length=2*$coffee_page_t+$coffee_name_length+6; $null="0x00";if ($complement) {$null="0xff";} #$tab="/t"; #optional tab string $tab=" "; #optional tabs or spaces at beginning of line, e.g. "\t\t" if (!open(OUTPUT, "> $outputfile")) {die "Aborted: Could not create output file $outputfile";} #awkward but could not figure out how to compare paths later unless the file exists -- dak print(OUTPUT "\n"); close($outputfile); use Cwd qw(abs_path); if (!open(OUTPUT, "> $outputfile")) {die "Aborted: Could not create output file $outputfile";} $outputfile=abs_path($outputfile); if ($directory eq "") { opendir(DIR, "."); @files = grep { !/^\./ && !/(CVS|~)/ } readdir(DIR); closedir(DIR); foreach $file (@files) { if(-d $file && $file !~ /^\./) { $directory=$file; break; } } } if ($directory eq "") {die "Aborted: No subdirectory in current directory";} if (!chdir("$directory")) {die "Aborted: Directory \"$directory\" does not exist!";} print "Processing directory $directory as root of coffee file system\n"; opendir(DIR, "."); @files = grep { !/^\./ && !/(CVS|~)/ } readdir(DIR); closedir(DIR); foreach $file (@files) { if(-d $file && $file !~ /^\./) { print "Adding subdirectory $file\n"; opendir(DIR, $file); @newfiles = grep { !/^\./ && !/(CVS|~)/ } readdir(DIR); closedir(DIR); # printf "Adding files @newfiles\n"; @files = (@files, map { $_ = "$file/$_" } @newfiles); next; } } print "Writing to $outputfile\n"; print(OUTPUT "/**************Generated by /tools/avr-makecoffeedata*****************/\n"); print(OUTPUT "/*For coffee filesystem of sector size $coffee_sector_size and header length $coffee_header_length bytes*/\n"); $n=0;$coffeesize=0;$coffeesectors=0; foreach $file (@files) {if(-f $file) { if (length($file)>($coffee_name_length-1)) {die "Aborted: File name $file is too long";} if (abs_path("$file") eq abs_path("$outputfile")) { print "Skipping $outputfile for recursive input\n"; next; } print "Adding $file\n"; open(FILE, $file) || die "Aborted: Could not open file $file\n"; if (grep /.png/,$file) {binmode FILE;} if (grep /.jpg/,$file) {binmode FILE;} if (grep /.gif/,$file) {binmode FILE;} $file =~ s-^-/-; $fvar = $file; $fvar =~ s-/-_-g; $fvar =~ s-\.-_-g; $file_length= -s FILE; $coffee_sectors=int(($coffee_header_length+$file_length+$coffee_sector_size-1)/$coffee_sector_size); # $coffee_sectors=sprintf("%.0f",($coffee_header_length+$file_length+$coffee_sector_size-1)/$coffee_sector_size)-1; $coffee_length=$coffee_sectors*$coffee_sector_size; $flen[$n]=$file_length; $clen[$n]=$coffee_length; $n++;$coffeesectors+=$coffee_sectors;$coffeesize+=$coffee_length; if ($coffeesectors>$coffeemax) { print "Warning: sector number $coffeesectors overflows allocated sector size in coffee header\n"; } print(OUTPUT "\n__attribute__ ((section (\"$sectionname\")))\n"); print(OUTPUT "volatile const char data".$fvar."[$coffee_length] = {\n"); print(OUTPUT "$tab/* $file */\n$tab "); #--------------------Header----------------------------- #log_page for($j=0;$j<$coffee_page_t;$j++) {print (OUTPUT "$null, ");} #log_records, log_record_size for($j=0;$j<4;$j++) {print (OUTPUT "$null, ");} #max_pages if ($complement) {$coffee_sectors=$coffee_sectors^0xffffffff;} if ($coffee_page_t==1) { printf(OUTPUT "0x%2.2x, ",($coffee_sectors )&0xff); } elsif ($coffee_page_t==2) { printf(OUTPUT "0x%2.2x, ",($coffee_sectors>> 8)&0xff); printf(OUTPUT "0x%2.2x, ",($coffee_sectors )&0xff); } elsif ($coffee_page_t==4) { printf(OUTPUT "0x%2.2x, ",($coffee_sectors>>24)&0xff); printf(OUTPUT "0x%2.2x, ",($coffee_sectors>>16)&0xff); printf(OUTPUT "0x%2.2x, ",($coffee_sectors>> 8)&0xff); printf(OUTPUT "0x%2.2x, ",($coffee_sectors )&0xff); } if ($complement) {$coffee_sectors=$coffee_sectors^0xffffffff;} #eof hint and flags if ($complement) { print(OUTPUT "0xff, 0xfc,\n$tab"); } else { print(OUTPUT "0x00, 0x03,\n$tab"); } #file name for($j = 0; $j < length($file); $j++) { $temp=unpack("C", substr($file, $j, 1)); if ($complement) {$temp=$temp^0xff;} printf(OUTPUT " %#02.2x,", $temp); } for(; $j < $coffee_name_length; $j++) {printf(OUTPUT " $null,");} print(OUTPUT "\n$tab"); #------------------File Data--------------------------- $coffee_length-=$coffee_header_length; $i = 0; while(read(FILE, $data, 1)) { $temp=unpack("C", $data); if ($complement) {$temp=$temp^0xff;} printf(OUTPUT " 0x%2.2x,", $temp); $i++;$coffee_length--; if($i == 10) { print(OUTPUT "\n$tab"); $i = 0; } } while (--$coffee_length>1) { print (OUTPUT " $null,"); if($i++ == 9) { print(OUTPUT "\n$tab"); $i = 0; } } print (OUTPUT " $null};\n"); close(FILE); push(@fvars, $fvar); push(@pfiles, $file); }} if ($linkedlist) { #-------------------httpd_fsdata_file links------------------- #The non-coffee PROGMEM flash file system for the Raven webserver uses a linked flash list as follows: print(OUTPUT "\n\n/* Structure of linked list (all offsets relative to start of section):\n"); print(OUTPUT "struct httpd_fsdata_file {\n"); print(OUTPUT "$tab const struct httpd_fsdata_file *next; //actual flash address of next link\n"); print(OUTPUT "$tab const char *name; //offset to coffee file name\n"); print(OUTPUT "$tab const char *data; //offset to coffee file data\n"); print(OUTPUT "$tab const int len; //length of file data\n"); print(OUTPUT "#if HTTPD_FS_STATISTICS == 1 //not enabled since list is in PROGMEM\n"); print(OUTPUT "$tab uint16_t count; //storage for file statistics\n"); print(OUTPUT "#endif\n"); print(OUTPUT "}\n*/\n"); # Note flash addresses above 0xffff require 32 bit pointers using pgm_read_byte_far, # and the coffee file system is above that. So the addresses start from 0 which # allows a 64K file system. The flash starting address is added in the read routine. print(OUTPUT "\n#define HTTPD_FS_ROOT file$fvars[$i - 1]\n\n"); for($i = 0; $i < @fvars; $i++) { $file = $pfiles[$i]; $fvar = $fvars[$i]; if($i == 0) { $prevfile = "NULL"; $data_offset=0; } else { $data_offset=$data_offset+$clen[$i-1]; $prevfile = "file" . $fvars[$i - 1]; } $filename_offset=$data_offset+6+2*$coffee_page_t; $coffee_offset=$data_offset+$coffee_header_length; if ($coffee_offset>0xffff) {print "Warning : Linked list offset field overflow\n";} print(OUTPUT "const struct httpd_fsdata_file"); for ($t=length($file);$t<16;$t++) {print(OUTPUT " ")}; # for AVR, add PROGMEM here print(OUTPUT " file".$fvar."[] PROGMEM={{"); for ($t=length($prevfile);$t<20;$t++) {print(OUTPUT " ")}; print(OUTPUT "$prevfile, "); printf(OUTPUT "(const char *)0x%4.4x, ",$filename_offset); printf(OUTPUT "(const char *)0x%4.4x, ",$coffee_offset); printf(OUTPUT "%5u}};\n",$flen[$i]); } print(OUTPUT "\n#define HTTPD_FS_NUMFILES $i\n"); } print "All done, preallocated coffee files occupy $coffeesize bytes\n";