[Tinyos-contrib-commits] CVS: tinyos-1.x/contrib/rincon/apps/Blackbook3/implementation GenericCrcC.nc, NONE, 1.1 NodeShopC.nc, NONE, 1.1 BBootC.nc, NONE, 1.1 BDictionaryC.nc, NONE, 1.1 DataWriterC.nc, NONE, 1.1 Blackbook.h, NONE, 1.1 BDictionaryM.nc, NONE, 1.1 BFileWriteM.nc, NONE, 1.1 NodeMapM.nc, NONE, 1.1 NodeMapC.nc, NONE, 1.1 BFileRead.h, NONE, 1.1 BFileDeleteC.nc, NONE, 1.1 BFileReadC.nc, NONE, 1.1 UtilC.nc, NONE, 1.1 SectorMapM.nc, NONE, 1.1 WriteAllocM.nc, NONE, 1.1 BFileWriteC.nc, NONE, 1.1 Checkpoint.h, NONE, 1.1 CheckpointM.nc, NONE, 1.1 EmptyCheckC.nc, NONE, 1.1 CheckpointC.nc, NONE, 1.1 DataWriterM.nc, NONE, 1.1 BFileDirC.nc, NONE, 1.1 BFileDeleteM.nc, NONE, 1.1 BBootM.nc, NONE, 1.1 EmptyCheckM.nc, NONE, 1.1 GenericCrcM.nc, NONE, 1.1 BCleanC.nc, NONE, 1.1 BlackbookConst.h, NONE, 1.1 BFileReadM.nc, NONE, 1.1 DataReaderM.nc, NONE, 1.1 BDictionary.h, NONE, 1.1 UtilM.nc, NONE, 1.1 BFileWrite.h, NONE, 1.1 DataReaderC.nc, NONE, 1.1 WriteAllocC.nc, NONE, 1.1 SectorMapC.nc, NONE, 1.1 NodeShopM.nc, NONE, 1.1 BFileDirM.nc, NONE, 1.1 BCleanM.nc, NONE, 1.1

dmm rincon at users.sourceforge.net
Thu Apr 20 16:03:34 PDT 2006


Update of /cvsroot/tinyos/tinyos-1.x/contrib/rincon/apps/Blackbook3/implementation
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv16174/contrib/rincon/apps/Blackbook3/implementation

Added Files:
	GenericCrcC.nc NodeShopC.nc BBootC.nc BDictionaryC.nc 
	DataWriterC.nc Blackbook.h BDictionaryM.nc BFileWriteM.nc 
	NodeMapM.nc NodeMapC.nc BFileRead.h BFileDeleteC.nc 
	BFileReadC.nc UtilC.nc SectorMapM.nc WriteAllocM.nc 
	BFileWriteC.nc Checkpoint.h CheckpointM.nc EmptyCheckC.nc 
	CheckpointC.nc DataWriterM.nc BFileDirC.nc BFileDeleteM.nc 
	BBootM.nc EmptyCheckM.nc GenericCrcM.nc BCleanC.nc 
	BlackbookConst.h BFileReadM.nc DataReaderM.nc BDictionary.h 
	UtilM.nc BFileWrite.h DataReaderC.nc WriteAllocC.nc 
	SectorMapC.nc NodeShopM.nc BFileDirM.nc BCleanM.nc 
Log Message:
Uploaded the Blackbook file system, version 3.

--- NEW FILE: GenericCrcC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */


/**
 * GenericCrc configuration
 * Uses the same CRC algorithm as BlockStorage.
 * @author David Moss (dmm at rincon.com)
 */
 
configuration GenericCrcC {
  provides {
    interface GenericCrc;
  }
}

implementation {
  components GenericCrcM;
  
  GenericCrc = GenericCrcM;
}


--- NEW FILE: NodeShopC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

 
/**
 * Blackbook NodeShop Configuration
 * Use the unique("NodeShop") parameterized interface
 * when connecting to this module.
 *
 * NodeShop is controlled by the NodeMap to 
 * convert and write nodes to metadata on flash.
 * It also calculates the CRC of nodes already
 * written to flash.
 *
 * @author David Moss (dmm at rincon.com)
 */
 
includes Blackbook;

configuration NodeShopC {
  provides {
    interface NodeShop[uint8_t id];
    interface StdControl;
  }
}

implementation {
  components NodeShopM, NodeMapC, SectorMapC, StateC, UtilC;
  components FlashBridgeC;
  
  NodeShop = NodeShopM;
  
  StdControl = StateC;
  StdControl = NodeMapC;
  StdControl = FlashBridgeC;
 
  NodeShopM.SectorMap -> SectorMapC;
  NodeShopM.NodeMap -> NodeMapC; 
  NodeShopM.Util -> UtilC;
  NodeShopM.State -> StateC.State[unique("State")];
  NodeShopM.FlashBridge -> FlashBridgeC.FlashBridge[unique("FlashBridge")];
}



--- NEW FILE: BBootC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook Boot configuration
 */
 
includes Blackbook;

configuration BBootC {
  provides {
    interface BBoot;
    interface StdControl;
  }
}

implementation {
  components BBootM, NodeMapC, NodeShopC, SectorMapC, FlashBridgeC, CheckpointC, StateC, GenericCrcC, UtilC;
  
  BBoot = BBootM;
  
  StdControl = FlashBridgeC;
  StdControl = CheckpointC;
  StdControl = BBootM;
  StdControl = StateC;
  StdControl = NodeShopC;
  
  BBootM.GenericCrc -> GenericCrcC;
  BBootM.NodeBooter -> NodeMapC;
  BBootM.SectorMap -> SectorMapC;
  BBootM.NodeMap -> NodeMapC;
  BBootM.FlashBridge -> FlashBridgeC.FlashBridge[unique("FlashBridge")];
  BBootM.Checkpoint -> CheckpointC;
  BBootM.NodeShop -> NodeShopC.NodeShop[unique("NodeShop")];
  BBootM.PassState -> StateC.State[unique("State")];
  BBootM.CommandState -> StateC.State[unique("State")];
  BBootM.BlackbookState -> StateC.State[BLACKBOOK_STATE];
  BBootM.Util -> UtilC;
  
}



--- NEW FILE: BDictionaryC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook BDictionary configuration
 * Allows an application to store and retrieve key-value pairs on flash.
 * @author David Moss - dmm at rincon.com
 */

configuration BDictionaryC {
  provides {
    interface StdControl;
    interface BDictionary[uint8_t id];
  }
}

implementation {
  components BDictionaryM, GenericCrcC, WriteAllocC, NodeMapC, NodeShopC, StateC, FlashBridgeC, UtilC;

  StdControl = FlashBridgeC;
  StdControl = BDictionaryM;
  StdControl = WriteAllocC;
  StdControl = StateC;
  
  BDictionary = BDictionaryM;
  
  BDictionaryM.NodeMap -> NodeMapC;
  BDictionaryM.NodeShop -> NodeShopC.NodeShop[unique("NodeShop")];
  BDictionaryM.FlashBridge -> FlashBridgeC.FlashBridge[unique("FlashBridge")];
  BDictionaryM.GenericCrc -> GenericCrcC;
  BDictionaryM.WriteAlloc -> WriteAllocC;
  BDictionaryM.BlackbookState -> StateC.State[BLACKBOOK_STATE];
  BDictionaryM.CommandState -> StateC.State[unique("State")];
  BDictionaryM.DictionaryState -> StateC.State[unique("State")];
  BDictionaryM.SearchState -> StateC.State[unique("State")];
  BDictionaryM.Util -> UtilC;
}



--- NEW FILE: DataWriterC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook DataWriter Configuration
 * Writes data to the write address of a node,
 * and updates the node's dataCrc. This does not
 * flush the data to flash.
 *
 * @author David Moss - dmm at rincon.com
 */
 
configuration DataWriterC {
  provides {
    interface StdControl;
    interface DataWriter;
  }
}

implementation {
  components DataWriterM, FlashBridgeC, GenericCrcC;
  
  DataWriter = DataWriterM;
  StdControl = FlashBridgeC;
  
  DataWriterM.FlashBridge -> FlashBridgeC.FlashBridge[unique("FlashBridge")];
  DataWriterM.GenericCrc -> GenericCrcC;
}




--- NEW FILE: Blackbook.h ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook Definitions v.3
 * @author David Moss (dmm at rincon.com)
 */

#include "BlackbookConst.h"

/** 
 * This is a complete name of a file that we can pass around Blackbook
 * and not worry about whether or not enough bytes were allocated for
 * the filename.
 */
typedef struct filename {

  /** The name of a file */
  char getName[FILENAME_LENGTH];

} filename;


/** 
 * This is the nodemeta information kept at the start of each 
 * node on flash
 */
typedef struct nodemeta {

  /** The CRC of the filename this node is associated with */
  uint16_t filenameCrc;
  
  /** The element of the file this node represents, 0 for the first node */
  uint16_t fileElement;
  
  /** Length of the data for this node on flash */
  uint16_t dataLength;

  /** CRC of the node on flash - this must be located after dataLength */
  uint16_t dataCrc;

  /** Magic number to detect valid metadata. This is after dataCrc for finalizing */
  uint16_t magicNumber;
  
} nodemeta;


/** 
 * This is the filemeta information located directly after nodemeta
 * information for the first node of a file on flash
 */
typedef struct filemeta {

  /** Name of the file */
  struct filename name;
  
} filemeta;


/** 
 * This is the node information kept in memory for each node
 */
typedef struct node {
  /** The address of this node on flash */
  uint32_t flashAddress;
  
  /** The next node in the file after this one */
  struct node *nextNode;
  
  /** The total length of valid data written to this node */
  uint16_t dataLength;

  /** The total length of space reserved for this node */
  uint16_t reserveLength;
  
  /** The current CRC of the node */
  uint16_t dataCrc;

  /** The CRC of the filename from the file this node is associated with */
  uint16_t filenameCrc;
  
  /** The state of the node */
  uint8_t state;

  /** The parameterized interface this file is dedicated to */
  uint8_t fileElement;

} node;


/** 
 * This is the information kept for each file in RAM memory 
 */
typedef struct file {

  /** The first node of this file */
  node *firstNode;
  
  /** The calculated crc of the file, so we don't have to calculate it every time */
  uint16_t filenameCrc;
  
  /** The state of this file */
  uint8_t state;
  
  /** The type of data contained in this node */
  uint8_t type;
  
  /** Name of this file */
  struct filename name;

} file;


/**
 * This is the sector information kept
 * in RAM for every sector on flash.
 */
typedef struct flashsector {
  
  /** Total number of unfinalized nodes. uint16_t only for word alignment */
  uint16_t totalUnfinalizedNodes;
  
  /** Next page number available for writing */
  uint16_t writePage;
  
  /** Total amount of valid nodes on this sector */
  uint8_t totalNodes;
  
  /** This sector's volume ID, and start address from calculation */
  uint8_t index;
  
  /** FALSE if this sector has no open write files */
  bool inUse;
  
} flashsector;



/**
 * This is the checkpoint struct that is inserted
 * into a key-value pair in the Checkpoint file
 */
typedef struct checkpoint {

  /** Node's filename CRC for verification */
  uint16_t filenameCrc;
  
  /** The CRC of the data contained up to the dataLength of the node */
  uint16_t dataCrc;

  /** Length of the node's data */ 
  uint16_t dataLength;
  
} checkpoint;



/** Possible node types */
enum {
  TYPE_BINARY,
  TYPE_KEYVALUE,
  TYPE_CHECKPOINT,
};

/** Possible node States */
enum {
  /** The node can be used by anything needed a node */
  NODE_EMPTY,
  
  /** The node contains valid information but is not being used */
  NODE_IDLE,
  
  /** The node is in use, and it remains unfinalized on flash */
  NODE_WRITING_UNFINALIZED,
  
  /** The node is not in use, but remains unfinalized on flash */
  NODE_CLOSED_UNFINALIZED,
  
  /** The node is open for interaction, probably a keyvalue or checkpoint node */
  NODE_OPEN,

  /** This node exists virtually, but no info has been written to flash */
  NODE_TEMPORARY,
  
  /** This node has not been linked in with its file */
  NODE_BOOTING,
  
  /** This node was found on flash, but is not valid */
  NODE_DELETED,
};

/** Possible node States */
enum {
  /** This file index is empty and can be used by anybody */
  FILE_EMPTY,
  
  /** This file exists virtually, but no info has been written to flash */
  FILE_TEMPORARY,
  
  /** This file is valid but not being used */
  FILE_IDLE,
  
  /** The nodes in this file is open for reading */
  FILE_READING,
  
  /** The nodes in this file are open for writing */
  FILE_WRITING,
  
  /** This file is being deleted */
  FILE_DELETING,
  
  /** This file is not yet linked in with its nodes */
  FILE_BOOTING,
  
  /** This file was found on flash but is invalid */
  FILE_DELETED,
  
};


/** Magic Words */
enum {
  /** No node exists at this point in the flash */
  META_EMPTY = 0xFFFF,             // binary 1111
  
  /** This node was open during the last shut down */
  META_WRITING_UNFINALIZED = 0x7777,  // binary 0111
  
  /** 
   * The dataLength of this node can be found in the checkpoint, and its
   * reserveLength is at the next page boundary in flash after the dataLength
   */
  META_CLOSED_UNFINALIZED = 0x7733,
  
  /** This node is finalized on flash and all information is local */
  META_VALID = 0x3333,             // binary 0011
  
  /** This node is deleted, mark up the SectorMap and move on */
  META_INVALID = 0x1111,           // binary 0001

  /** This is a checkpoint file that wasn't done being created, delete it */
  META_CHECKPOINT_CONSTRUCTING = 0x7771,
  
  /** This is a valid checkpoint file. */
  META_CHECKPOINT_VALID = 0x7171,
  
  /** This is a dictionary file that wasn't done being created, delete it */
  META_KEYVALUE_CONSTRUCTING = 0x3331,    
  
  /** This is a valid dictionary file */
  META_KEYVALUE_VALID = 0x3131,
  
  /** This is the type of data you'll find when a dataCrc is unfinalized */
  UNFINALIZED_CRC = 0xFFFF,
  
  /** This is the type of data you'll find when a dataLength is unfinalized */
  UNFINALIZED_DATA = 0xFFFF,
};

/** Global state machine for blackbook */
enum {
  S_IDLE = 0,
  
  /** The file system is booting */
  S_BOOT_BUSY,
  
  /** The dictionary is in use */
  S_DICTIONARY_BUSY,
  
  /** The file writer is in use */
  S_WRITE_BUSY,
  
  /** The file reader is in use */
  S_READ_BUSY,
  
  /** The file dir is in use */
  S_DIR_BUSY,
  
  /** The file delete is in use */
  S_DELETE_BUSY,
  
  
  BLACKBOOK_STATE = unique("State"),
};



--- NEW FILE: BDictionaryM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
[...1489 lines suppressed...]
  }

  default event void BDictionary.closed[uint8_t id](result_t result) {
  }

  default event void BDictionary.inserted[uint8_t id](uint32_t key, void *value, uint16_t valueSize, result_t result) {
  }

  default event void BDictionary.retrieved[uint8_t id](uint32_t key, void *valueHolder, uint16_t valueSize, result_t result) {
  }

  default event void BDictionary.removed[uint8_t id](uint32_t key, result_t result) {
  }
  
  default event void BDictionary.nextKey[uint8_t id](uint32_t nextKey, result_t result) {
  }
  
  
}


--- NEW FILE: BFileWriteM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */


/**
 * Blackbook BFileWrite Configuration
 * Open, Write, Save, Close Blackbook files.
 * Use unique("BFileWrite") when connecting to a parameterized
 * interface.
 * @author David Moss - dmm at rincon.com
 */
 
includes Blackbook;
includes BFileWrite;

module BFileWriteM {
  provides {
    interface StdControl;
    interface BFileWrite[uint8_t id];
  }
  
  uses {
    interface State as BlackbookState;
    interface State as CommandState;
    interface WriteAlloc;
    interface DataWriter;
    interface NodeMap;
    interface SectorMap;
    interface NodeShop;
    interface Checkpoint;
    interface Util;
  }
}

implementation {

  /** Each client's file writing information */
  filewriter writers[uniqueCount("BFileWrite")];
  
  /** The current client we're working with */
  uint8_t currentClient;
  
  /** The total amount of data to write to the file */
  uint16_t currentTotal;
  
  /** The current amount of data to append to the node */
  uint16_t appendAmount;
  
  /** The amount of data actually written to the file */
  uint16_t totalBytesWritten;
  
  /** A pointer to the data to write to the file */
  void *currentData;
  
  
  /** Command States */
  enum {
    S_IDLE = 0,
    S_COMMAND_OPEN,
    S_COMMAND_CLOSE,
    S_COMMAND_SAVE,
    S_COMMAND_APPEND,
    
  };
  
  
  /***************** Prototypes ****************/
  /** Close all nodes in the current client's open file */
  task void closeFile();
  
  /** Finalize the current client's open node */
  task void finalizeNode();
  
  /** Unfinalize the current client's open node to flash */
  task void unfinalizeNewNode();
  
  /** Checkpoint the current client's open node */
  task void checkpointNode();
  
  /** Append data to the current client's file until all nodes are full */
  task void appendFile();
  
  /** Output the appendAmount of data to the current client's open node */
  task void outputData();
  
  
  /***************** StdControl Commands ****************/
  command result_t StdControl.init() {
    int i;
    for(i = 0; i < uniqueCount("BFileWrite"); i++) {
      writers[i].openFile = NULL;
    }
    return SUCCESS;
  }
  
  command result_t StdControl.start() {
    return SUCCESS;
  }
  
  command result_t StdControl.stop() {
    return SUCCESS;
  }
  
  
  /***************** BFileWrite Commands ****************/
  /**
   * Open a file for writing. 
   * 
   * The reservedBytes must be specified to ensure enough memory
   * exists in flash for the operation. This does not necessarily
   * reflect how many bytes must be written - if less bytes
   * are actually written, the physical file size is automatically
   * adjusted to the nearest page boundary when the file is finalized.
   * For example, to create a log file that will be able to hold a 
   * maximum of 64000 bytes, specify the reserveBytes to be 64000. 
   * Then if you only log 32000 bytes and close the file, the 
   * physical file size on flash will be around 32k on flash 
   * instead of 64k.
   * 
   * If the file does already exist on flash, 
   * giving a reserveBytes value that is less than the existing
   * file size will not affect the original file size.  This may give you only
   * a few bytes of free space to write to, but only a maximum of a page
   * of flash.  You can use BFileDir.getDataLength(char *filename) to 
   * get the length of an existing file. So say you have
   * a file already written and closed, and you expect
   * to write 0x1000 more bytes to it:
   *
   * call BFileWrite.open("myFile", 
   *     call BFileDir.getDataLength("myFile") + 0x1000);
   *
   * @param fileName - name of the file to write to
   * @param minimumSize The minimum requested amount of total space
   *            to reserve in the file.  The physical size on the
   *            flash may be more by one page of flash.
   */ 
  command result_t BFileWrite.open[uint8_t id](char *fileName, uint32_t minimumSize) {
    filename currentFilename;
    
    if(!call BlackbookState.requestState(S_WRITE_BUSY)) {
      return FAIL;
    }
    
    currentClient = id;
    
    if(writers[currentClient].openFile != NULL) {
      call BlackbookState.toIdle();
      return FAIL;
    }
    
    call Util.filenameCpy(&currentFilename, fileName);
    call CommandState.forceState(S_COMMAND_OPEN);
    if(!call WriteAlloc.openForWriting(&currentFilename, minimumSize, TYPE_BINARY, FALSE)) {
      call CommandState.toIdle();
      call BlackbookState.toIdle();
      return FAIL;
    }
    return SUCCESS;
  }

  /**
   * Close any currently opened write file.
   */
  command result_t BFileWrite.close[uint8_t id]() {
    if(!call BlackbookState.requestState(S_WRITE_BUSY)) {
      return FAIL;
    }
    
    currentClient = id;
    
    if(writers[currentClient].openFile == NULL) {
      call BlackbookState.toIdle();
      signal BFileWrite.closed[currentClient](SUCCESS);
      return SUCCESS;
    }
    
    call CommandState.forceState(S_COMMAND_CLOSE);
    post closeFile();
    return SUCCESS;
  }

  /**
   * Save the current state of the file, guaranteeing the next time
   * we experience a catastrophic failure, we will at least be able to
   * recover data from the open write file up to the point
   * where save was called.
   *
   * If data is simply being logged for a long time, use save() 
   * periodically but probably more infrequently.
   *
   * @return SUCCESS if the currently open file will be saved.
   */
  command result_t BFileWrite.save[uint8_t id]() {
    if(!call BlackbookState.requestState(S_WRITE_BUSY)) {
      return FAIL;
    }
    
    currentClient = id;
    
    if(writers[currentClient].openFile == NULL) {
      call BlackbookState.toIdle();
      return FAIL;
    }
    
    call CommandState.forceState(S_COMMAND_SAVE);
    post checkpointNode();
    return SUCCESS;
  }

  /**
   * Append the specified amount of data from a given buffer
   * to the open write file.  
   *
   * @param buf - the buffer of data to append
   * @param amount - the amount of data in the buffer to write.
   * @return SUCCESS if the data will be written, FAIL if there
   *     is no open file to write to.
   */ 
  command result_t BFileWrite.append[uint8_t id](void *data, uint16_t amount) {
    if(!call BlackbookState.requestState(S_WRITE_BUSY)) {
      return FAIL;
    }
    
    currentClient = id;
    
    if(writers[currentClient].openFile == NULL) {
      call BlackbookState.toIdle();
      return FAIL;
    }
    
    totalBytesWritten = 0;
    currentTotal = amount;
    currentData = data;
    
    call CommandState.forceState(S_COMMAND_APPEND);
    post appendFile();
    return SUCCESS;
  }

  /**
   * Obtain the remaining bytes available to be written in this file
   * This is the total reserved length minus your current 
   * write position
   * @return the remaining length of the file.
   */
  command uint32_t BFileWrite.getRemaining[uint8_t id]() {
    if(writers[id].openFile != NULL) {
      return call NodeMap.getReserveLength(writers[id].openFile) - call NodeMap.getDataLength(writers[id].openFile);
   
    } else {
      return 0;
    }
  }

  /**
   * Find if the specified file is available for writing,
   * and the current client has no other files open 
   * for writing that would prevent this file from being opened.
   * @param filename - name of the file
   * @return TRUE if the file can be opened for writing.
   *
  command bool BFileWrite.canOpen[uint8_t id](char *fileName) {
    filename currentFilename;
    
    if(writers[id].openFile != NULL) {
      return FALSE;
    }
    
    call Util.filenameCpy(&currentFilename, fileName);
    return (call NodeMap.getFile(&currentFilename))->state == FILE_IDLE;
  }
  
  /***************** WriteAlloc Events ****************/
  /**
   * The write open process completed
   * @param openFile - the file that was opened for writing 
   * @param writeNode - the node to write to
   * @param result - SUCCESS if the file was correctly opened
   */
  event void WriteAlloc.openedForWriting(file *openFile, node *writeNode, result_t result) {
    if(call CommandState.getState() == S_COMMAND_OPEN) {
      if(!result) {
        call CommandState.toIdle();
        call BlackbookState.toIdle();
        signal BFileWrite.opened[currentClient](NULL, 0, FAIL);
        return;
      }
      
      if(openFile->type != TYPE_BINARY) {
        openFile->state = FILE_IDLE;
        writeNode->state = NODE_IDLE;
        call CommandState.toIdle();
        call BlackbookState.toIdle();
        signal BFileWrite.opened[currentClient](NULL, 0, FAIL);
        return;
      }
      
      writers[currentClient].openFile = openFile;
      writers[currentClient].openNode = writeNode;
      call CommandState.toIdle();
      call BlackbookState.toIdle();
      signal BFileWrite.opened[currentClient]((char *) &openFile->name.getName, call NodeMap.getReserveLength(openFile), SUCCESS);
    }
  }
  
  
  /***************** DataWriter Events ****************/
  /**
   * Data was appended to the node in the flash.
   * @param dataWritten - pointer to the buffer containing the data written
   * @param amountWritten - the amount of data appended to the node.
   * @param result - SUCCESS if the data was successfully written
   */
  event void DataWriter.dataWriteDone(void *dataWritten, uint32_t amountWritten, result_t result) {
    if(!result) {
      post outputData();
      return;
    }
      
    totalBytesWritten += amountWritten;
    post appendFile();
  }
  
  
  /***************** NodeShop Events *****************/
  /**
   * Unfinalized metadata was written to flash.
   * @param focusedNode - the node that metadata was written for.
   * @param focusedFile - the file that metadata was written for.
   * @param dataStartAddress - the address where data can start being written.
   * @param result - SUCCESS if the metadata was correctly written.
   */
  event void NodeShop.unfinalizedMetaWritten(node *focusedNode, file *focusedFile, uint32_t dataStartAddress, result_t result) {
    if(call CommandState.getState() == S_COMMAND_APPEND) {
      if(!result) {
        post unfinalizeNewNode();
        return;
      }
      
      post appendFile();
    }
  }
  
  /**
   * Finalized metadata was written to flash. This node
   * can no longer be written to.
   * @param focusedNode - the node that metadata was finalized for
   * @param result - SUCCESS if the metadata was correctly finalized.
   */
  event void NodeShop.finalizedMetaWritten(node *focusedNode, result_t result) {
    if(!result) {
      post finalizeNode();
      return;
    }
    
    // Remove this node's checkpoint
    post checkpointNode();
  }
  
  /**
   * A node was checkpointed and the magic number was
   * updated on flash.
   * @param focusedNode - the node that is now closed and unfinalized on flash
   * @param result - SUCCESS if the node was correctly closed
   */
  event void NodeShop.closedUnfinalizedMetaWritten(node *focusedNode, result_t result) {

  }
  
  /**
   * A node was deleted from flash by marking its magic number
   * invalid in the metadata.
   * @param focusedNode - the node that was deleted.
   * @param result - SUCCESS if the node was deleted successfully.
   */
  event void NodeShop.metaDeleted(node *focusedNode, result_t result) {
  }
 
  /**
   * A crc was calculated from node data on flash
   * @param dataCrc - the crc of the data read from the node on flash.
   * @param result - SUCCESS if the crc is valid
   */
  event void NodeShop.crcCalculated(uint16_t dataCrc, result_t result) {
  }
  
  /***************** Checkpoint Events ****************/
  /**
   * The given node was updated in the Checkpoint
   * @param focusedNode - the node that was updated
   * @param result - SUCCESS if everything's ok
   */
  event void Checkpoint.updated(node *focusedNode, result_t result) {
    if(call BlackbookState.getState() == S_WRITE_BUSY) {
      node *nextNode;
      if(!result) {
        post checkpointNode();
        return;
      }
    
    
      if(call CommandState.getState() == S_COMMAND_SAVE) {
        call CommandState.toIdle();
        call BlackbookState.toIdle();
        signal BFileWrite.saved[currentClient]((char *) &writers[currentClient].openFile->name.getName, result);

      } else if(call CommandState.getState() == S_COMMAND_CLOSE) {
        // Keep the recently closed node, but deallocate the rest
      
        call SectorMap.free(writers[currentClient].openNode);
        nextNode = writers[currentClient].openNode->nextNode;
        writers[currentClient].openNode->nextNode = NULL;
      
        while(nextNode != NULL) {
          writers[currentClient].openNode = nextNode;
          call SectorMap.free(writers[currentClient].openNode);
          writers[currentClient].openNode->state = NODE_EMPTY;
          nextNode = writers[currentClient].openNode->nextNode;
          writers[currentClient].openNode->nextNode = NULL;
        }
      
        writers[currentClient].openNode = NULL;
        writers[currentClient].openFile->state = FILE_IDLE;
        writers[currentClient].openFile = NULL;
        call CommandState.toIdle();
        call BlackbookState.toIdle();
        signal BFileWrite.closed[currentClient](SUCCESS);

      } else if(call CommandState.getState() == S_COMMAND_APPEND) {
        writers[currentClient].openNode = writers[currentClient].openNode->nextNode;
        post unfinalizeNewNode();
      }
    }
  }
  
  /**
   * The checkpoint file was opened.
   * @param result - SUCCESS if it was opened successfully
   */
  event void Checkpoint.checkpointOpened(result_t result) {
  }
  
  /** 
   * A node was recovered.
   * @param result - SUCCESS if it was handled correctly.
   */
  event void Checkpoint.recovered(result_t result) {
  }
  
  /***************** Tasks ****************/
  /**
   * Append data to the open file's nodes 
   * until all nodes are full.
   */
  task void appendFile() {

    if(totalBytesWritten < currentTotal) {
      appendAmount = currentTotal - totalBytesWritten;
      
      if(writers[currentClient].openNode->reserveLength - writers[currentClient].openNode->dataLength < appendAmount) {
        appendAmount = writers[currentClient].openNode->reserveLength - writers[currentClient].openNode->dataLength;

        if(appendAmount == 0) {
          // This node is full.
          if(writers[currentClient].openNode->nextNode != NULL) {
            // There are more nodes available in the file
            post finalizeNode();
            return;
          
          } else {
            // There are no more available in the file.
            call CommandState.toIdle();
            call BlackbookState.toIdle();
            signal BFileWrite.appended[currentClient]((char *) &writers[currentClient].openFile->name.getName, currentData, totalBytesWritten, SUCCESS);
            return;
          }
        }
      }
      
      // This node has space available for writing
      post outputData();
      
    } else {
      // No more data to write!
      call CommandState.toIdle();
      call BlackbookState.toIdle();
      signal BFileWrite.appended[currentClient]((char *) &writers[currentClient].openFile->name.getName, currentData, totalBytesWritten, SUCCESS);
    }    
  }
  
  /**
   * Output the appendAmount of data to the current client's node
   */
  task void outputData() {
    if(!call DataWriter.writeData(writers[currentClient].openNode, writers[currentClient].openFile, currentData + totalBytesWritten, appendAmount)) {
      post outputData();
    }
  }
  
  /**
   * Close all nodes in the current client's file
   */
  task void closeFile() {
    node *lastNode = NULL;
    
    writers[currentClient].openNode = writers[currentClient].openFile->firstNode;
    
    do {
      if(writers[currentClient].openNode->state != NODE_IDLE) {
       
        if(writers[currentClient].openNode->state == NODE_TEMPORARY) {
          if(lastNode != NULL) {
            lastNode->nextNode = NULL;
          }
          writers[currentClient].openNode->state = NODE_EMPTY;
          
        } else if(writers[currentClient].openNode->state == NODE_WRITING_UNFINALIZED) {
          if(writers[currentClient].openNode->dataLength == writers[currentClient].openNode->reserveLength) {
            post finalizeNode();
            
          } else {
            // When closing an open unfinalized node, set its reserve length and free the sector.
            if(writers[currentClient].openNode == writers[currentClient].openFile->firstNode) {
              writers[currentClient].openNode->reserveLength = call Util.getNextPageAddress(writers[currentClient].openNode->dataLength + (sizeof(nodemeta) + sizeof(filemeta) - 1)) - (sizeof(nodemeta) + sizeof(filemeta));
            } else {
              writers[currentClient].openNode->reserveLength = call Util.getNextPageAddress(writers[currentClient].openNode->dataLength + sizeof(nodemeta) - 1) - sizeof(nodemeta);
            }
            
            post checkpointNode();
          }
          return;
          
        }
      }
    } while((writers[currentClient].openNode = writers[currentClient].openNode->nextNode) != NULL);
    
    writers[currentClient].openFile->state = FILE_IDLE;
    writers[currentClient].openFile = NULL;
    call CommandState.toIdle();
    call BlackbookState.toIdle();
    signal BFileWrite.closed[currentClient](SUCCESS);
  }
  
  
  /**
   * Finalize the open node from the current client's open file
   */
  task void finalizeNode() {
    if(!call NodeShop.writeFinalizedNode(writers[currentClient].openNode, writers[currentClient].openFile)) {
      post finalizeNode();
    }
  }
  
  /**
   * Unfinalize the current client's open node to flash
   */
  task void unfinalizeNewNode() {
    if(!call NodeShop.writeUnfinalizedNode(writers[currentClient].openNode, writers[currentClient].openFile)) {
      post unfinalizeNewNode();
    }
  }
  
  
  /**
   * Checkpoint the open node from the current client
   */
  task void checkpointNode() {
    if(!call Checkpoint.update(writers[currentClient].openNode)) {
      post checkpointNode();
    }
  }
  
  
  /***************** Functions ****************/
  
  /***************** Defaults ****************/  
  default event void BFileWrite.opened[uint8_t id](char *fileName, uint32_t len, result_t result) {
  }

  default event void BFileWrite.closed[uint8_t id](result_t result) {
  }

  default event void BFileWrite.saved[uint8_t id](char *fileName, result_t result) {
  }

  default event void BFileWrite.appended[uint8_t id](char *fileName, void *data, uint16_t amountWritten, result_t result) {
  }
}


--- NEW FILE: NodeMapM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook NodeMap Module
 * Allows access to general file system information through NodeMap interface
 * File system modifications through NodeBooter interface
 * File allocation and services through NodeLoader interface
 * @author David Moss (dmm at rincon.com)
 */
 
includes Blackbook;

module NodeMapM {
  provides {
    interface NodeMap;
    interface NodeBooter;
    interface StdControl;
  }
  
  uses {
    interface Util;
  }
}

implementation {

  /** The array of files in memory */
  file files[MAX_FILES];
  
  /** The array of nodes in memory */
  node nodes[MAX_FILES*NODES_PER_FILE];
  
  
  /***************** StdControl Commands ****************/
  command result_t StdControl.init() {
    int i; 

    for(i = 0; i < call NodeMap.getMaxNodes(); i++) {
      nodes[i].filenameCrc = 0;
      nodes[i].nextNode = NULL;
      nodes[i].state = NODE_EMPTY;
    }
    
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      files[i].filenameCrc = 0;
      files[i].firstNode = NULL;
      files[i].state = FILE_EMPTY;
    }
    
    return SUCCESS;
  }
  
  command result_t StdControl.start() {
    return SUCCESS;
  }
  
  command result_t StdControl.stop() {
    return SUCCESS;
  }
  
  
  /***************** NodeBooter Commands *****************/
  /**
   * Request to add a node to the file system.
   * It is the responsibility of the calling function
   * to properly setup:
   *   > flashAddress
   *   > dataLength
   *   > reserveLength
   *   > dataCrc
   *   > filenameCrc
   *   > fileElement
   * 
   * Unless manually linked, state and nextNode are handled by NodeMap.
   * @return a pointer to an empty node if one is available
   *     NULL if no more exist
   */
  command node *NodeBooter.requestAddNode() {
    int i;

    for(i = 0; i < call NodeMap.getMaxNodes(); i++) {
      if(nodes[i].state == NODE_EMPTY) {
        nodes[i].nextNode = NULL;
        return &nodes[i];
      }
    }
    return NULL;
  }
  
  /**
   * Request to add a file to the file system
   * It is the responsibility of the calling function
   * to properly setup:
   *   > filename
   *   > filenameCrc
   *   > type
   *
   * Unless manually linked, state and nextNode are handled in NodeMap.
   * @return a pointer to an empty file if one is available
   *     NULL if no more exist
   */
  command file *NodeBooter.requestAddFile() {
    int i;
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      if(files[i].state == FILE_EMPTY) {
        files[i].firstNode = NULL;
        return &files[i];
      }
    }
    return NULL;
  }
  
  /**
   * After booting, the nodes loaded from flash must
   * be corrected linked. 
   * The node.fileelement will represent the fileElement from the nodemeta
   * before the file is finished being linked.
   */
  command result_t NodeBooter.link() {
    int fileIndex;
    int nodeIndex;
    node *lastLinkedNode;
    uint8_t currentElement;
    
    // Link all files and nodes
    for(fileIndex = 0; fileIndex < call NodeMap.getMaxFiles(); fileIndex++) {
      if(files[fileIndex].state != FILE_EMPTY && files[fileIndex].state != FILE_WRITING) {
        files[fileIndex].state = FILE_IDLE;
        lastLinkedNode = NULL;
        currentElement = 0;
        
        // locate all nodes associated with the file in the correct order
        for(nodeIndex = 0; nodeIndex < call NodeMap.getMaxNodes(); nodeIndex++) {
          // Check out the next node
          if(nodes[nodeIndex].state != NODE_EMPTY) {
            // This node needs to be linked
            if(nodes[nodeIndex].filenameCrc == files[fileIndex].filenameCrc) {
              // and it belongs to the current file we're linking
              if(nodes[nodeIndex].fileElement == currentElement) {
                // in fact it's the next node we're looking for

                if(currentElement == 0) {
                  // This is the first node of the file
                  files[fileIndex].firstNode = &nodes[nodeIndex];
                } else {
                  // This is the next node of the file
                  lastLinkedNode->nextNode = &nodes[nodeIndex];
                }
                   
                nodes[nodeIndex].nextNode = NULL;
                lastLinkedNode = &nodes[nodeIndex];
                currentElement++;
              }
            }
          }
        }
      }
    }
    
    return SUCCESS;
  }
  
  /***************** NodeMap Commands ****************/
  /**
   * @return the maximum number of nodes allowed
   */
  command uint16_t NodeMap.getMaxNodes() {
    return MAX_FILES * NODES_PER_FILE;
  }
  
  /** 
   * @return the maximum number of files allowed
   */
  command uint8_t NodeMap.getMaxFiles() {
    return MAX_FILES;
  }
  
  /** 
   * @return the total nodes used by the file system
   */
  command uint16_t NodeMap.getTotalNodes() {
    int i;
    uint16_t totalNodes = 0;
    for(i = 0; i < call NodeMap.getMaxNodes(); i++) {
      if(nodes[i].state != NODE_EMPTY) {
        totalNodes++;
      }
    }
    return totalNodes;
  }
    
  
  /**
   * @return the total nodes allocated to the given file
   */
  command uint8_t NodeMap.getTotalNodesInFile(file *focusedFile) {
    node *currentNode;
    uint8_t totalNodes = 0;
   
    if((focusedFile != NULL) && focusedFile->state != FILE_EMPTY) {
      currentNode = focusedFile->firstNode;
      while(currentNode != NULL) {
        totalNodes++;
        currentNode = currentNode->nextNode;
      }
    }
    return totalNodes;
  }
  
  /**
   * @return the total files used by the file system
   */
  command uint8_t NodeMap.getTotalFiles() {
    int i;
    uint8_t totalFiles = 0;
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      if(files[i].state != FILE_EMPTY) {
        totalFiles++;
      }
    }
    return totalFiles;
  }
  
  /**
   * @return the node's position in a file, 0xFF if not valid
   */
  command uint8_t NodeMap.getElementNumber(node *focusedNode) {
    file *parentFile;
    
    if((parentFile = call NodeMap.getFileFromNode(focusedNode)) == NULL) {
      return 0xFF;
    }
    
    return call NodeMap.getElementNumberFromFile(focusedNode, parentFile);
  }
    
    
  /**
   * If you already know the file, this is faster than getElementNumber(..)
   * @return the node's position in the given file, 0xFF if not valid
   */
  command uint8_t NodeMap.getElementNumberFromFile(node *focusedNode, file *focusedFile) {
    node *currentNode;
    uint8_t elementNumber = 0;
    
    if(focusedNode == NULL || focusedFile == NULL 
        || focusedNode->state == NODE_EMPTY || focusedFile->state == FILE_EMPTY) {
      return 0xFF;
    }
    
    currentNode = focusedFile->firstNode;
   
    // 0 indexed
    while(currentNode != focusedNode && (currentNode = currentNode->nextNode) != NULL) {
      elementNumber++;
    }
    
    return elementNumber;
  }
  
  
  /**
   * @return the file with the given name if it exists, NULL if it doesn't
   */
  command file *NodeMap.getFile(filename *focusedFilename) {
    int i;
    uint16_t focusedFileCrc = call Util.filenameCrc(focusedFilename);
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      if(focusedFileCrc == files[i].filenameCrc && files[i].state != FILE_EMPTY) {
        return &files[i];
      }
    }
    return NULL;
  }
  
  /**
   * @return the file associated with the given node, NULL if n/a.
   */
  command file *NodeMap.getFileFromNode(node *focusedNode) {
    int i;
    if(focusedNode == NULL || focusedNode->state == NODE_EMPTY) {
      return NULL;
    }
    
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      if(focusedNode->filenameCrc == files[i].filenameCrc 
          && files[i].state != FILE_EMPTY) {
        return &files[i];
      }
    }
    return NULL;
  }
  
   
  /**
   * Traverse the files on the file system from
   * 0 up to (max files - 1).
   * If performing a DIR, be sure to hide
   * Checkpoint files on the way out.
   * @return the file at the given index
   */
  command file *NodeMap.getFileAtIndex(uint8_t fileIndex) {
    if(fileIndex < call NodeMap.getMaxFiles()) {
      return &files[fileIndex];
    }
    return NULL;
  }
  
  /**
   * Get a node at a given index
   * @return the node if it exists, NULL if it doesn't.
   */
  command node *NodeMap.getNodeAtIndex(uint8_t nodeIndex) {
    if(nodeIndex < call NodeMap.getMaxNodes()) {
      return &nodes[nodeIndex];
    }
    return NULL;
  }
  
  /** 
   * Get the total length of data of a given file
   * @return the length of the file's data
   */
  command uint32_t NodeMap.getDataLength(file *focusedFile) {
    node *focusedNode;
    uint32_t dataLength = 0;
    if((focusedFile != NULL) && focusedFile->state != FILE_EMPTY) {
      focusedNode = focusedFile->firstNode;
      while(focusedNode != NULL) {
        dataLength += focusedNode->dataLength;
        focusedNode = focusedNode->nextNode;
      }
    }
    return dataLength;
  }
  
  /**
   * @return the reserve length of all nodes in the file
   */
  command uint32_t NodeMap.getReserveLength(file *focusedFile) {
    node *focusedNode;
    uint32_t reserveLength = 0;
    if((focusedFile != NULL) && focusedFile->state != FILE_EMPTY) {
      focusedNode = focusedFile->firstNode;
      while(focusedNode != NULL) {
        reserveLength += focusedNode->reserveLength;
        focusedNode = focusedNode->nextNode;
      }
    }
    return reserveLength;
  }
  
  /**
   * @return TRUE if there exists another file with the same name
   */
  command bool NodeMap.hasDuplicate(file *focusedFile) {
    int i;
    uint8_t numFiles = 0;
    
    for(i = 0; i < call NodeMap.getMaxFiles(); i++) {
      if(files[i].state != FILE_EMPTY) {
        if(files[i].filenameCrc == focusedFile->filenameCrc) {
          numFiles++;
        }
      }
    }
    
    return (numFiles > 1);
  }
}


--- NEW FILE: NodeMapC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

 
/**
 * Blackbook NodeMap Configuration
 * Allows access to general file system information through NodeMap interface
 * File system modifications through NodeBooter interface
 * File allocation and services through NodeLoader interface
 * @author David Moss (dmm at rincon.com)
 */
 
includes Blackbook;

configuration NodeMapC {
  provides {
    interface NodeMap;
    interface NodeBooter;
    interface StdControl;
  }
}

implementation {
  components NodeMapM, StateC, UtilC;

  NodeMap = NodeMapM;
  NodeBooter = NodeMapM;
  
  StdControl = NodeMapM;
  StdControl = StateC;
  
  NodeMapM.Util -> UtilC;
  
}



--- NEW FILE: BFileRead.h ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */



typedef struct filereader {

  /** The current file open for writing, NULL if no file is open */
  file *openFile;
  
  /** The current node being written from the open write file */
  node *openNode;
  
  /** The position to read from in the current open node */
  uint16_t readPosition;
  
} filereader;



--- NEW FILE: BFileDeleteC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook Delete Configuration
 * Invalidate a file on flash
 * 
 * @author David Moss - dmm at rincon.com
 */
 
includes Blackbook;

configuration BFileDeleteC {
  provides {
    interface BFileDelete[uint8_t id];
    interface StdControl;
  }
}

implementation {
  components BFileDeleteM, NodeShopC, NodeMapC, StateC, CheckpointC, UtilC;
  
  BFileDelete = BFileDeleteM;
  
  StdControl = NodeShopC;
  StdControl = NodeMapC;
  StdControl = StateC;
  StdControl = CheckpointC;
  
  BFileDeleteM.NodeShop -> NodeShopC.NodeShop[unique("NodeShop")];
  BFileDeleteM.NodeMap -> NodeMapC;
  BFileDeleteM.Checkpoint -> CheckpointC;
  BFileDeleteM.BlackbookState -> StateC.State[BLACKBOOK_STATE];
  BFileDeleteM.Util -> UtilC;
}


--- NEW FILE: BFileReadC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook BFileRead Configuration
 * Open, Read, Close Blackbook files.
 * Use unique("BFileRead") when connecting to a parameterized
 * interface.
 * @author David Moss - dmm at rincon.com
 */

includes Blackbook;

configuration BFileReadC {
  provides {
    interface StdControl;
    interface BFileRead[uint8_t id];
  }
}

implementation {
  components BFileReadM, NodeMapC, DataReaderC, StateC, UtilC;

  StdControl = BFileReadM;
  StdControl = NodeMapC;
  StdControl = DataReaderC;
  StdControl = StateC;
  
  BFileRead = BFileReadM;
  
  BFileReadM.BlackbookState -> StateC.State[BLACKBOOK_STATE];
  BFileReadM.NodeMap -> NodeMapC;
  BFileReadM.DataReader -> DataReaderC;
  BFileReadM.Util -> UtilC;

}


--- NEW FILE: UtilC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

 
/**
 * Blackbook Util Configuration
 * Provides basic tools for Blackbook operation
 * @author David Moss (dmm at rincon.com)
 */
 
includes Blackbook;

configuration UtilC {
  provides {
    interface Util;
  }
}

implementation {
  components UtilM, GenericCrcC;
  
  Util = UtilM;
  
  UtilM.GenericCrc -> GenericCrcC;
}




--- NEW FILE: SectorMapM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook SectorMap Module
 * Keeps track of memory usage and reserves sectors for
 * writing, helps decide where to place new nodes.
 * This could use some optimization when convert flash addresses
 * into page numbers.
 * @author David Moss (dmm at rincon.com)
 */
 
includes Blackbook;
includes FlashSettings;

module SectorMapM {
  provides {
    interface SectorMap;
    interface StdControl;
  }
  
  uses {
    interface Util;
    interface NodeMap;
  }
}

implementation {

  /** Array of flashSectors on the flash */
  flashsector flashSectors[FLASH_TOTAL_SECTORS];
  
  /** Contains the last sector index we allocated new nodes to, to spread them around */
  uint8_t currentSectorIndex;
  
  /***************** StdControl Commands ****************/
  command result_t StdControl.init() {
    int i;
    for(i = 0; i < call SectorMap.getTotalSectors(); i++) {
      flashSectors[i].index = i;
      flashSectors[i].writePage = 0;
      flashSectors[i].inUse = TRUE;
      flashSectors[i].totalUnfinalizedNodes = 0;      
      flashSectors[i].totalNodes = 0;
    }
    
    currentSectorIndex = FLASH_FIRST_BLACKBOOK_SECTOR;
    return SUCCESS;
  }
  
  command result_t StdControl.start() {
    return SUCCESS;
  }
  
  command result_t StdControl.stop() {
    return SUCCESS;
  }
  
  
  /***************** SectorMap Commands ****************/
  /**
   * @return the total sectors on flash
   */
  command uint8_t SectorMap.getTotalSectors() {
    return FLASH_TOTAL_SECTORS;
  }
  
  /**
   * Obtain the largest sector that is not in use
   * on the flash.  We use a global to store the
   * current sector index we're evaluating
   * so that each call to this function will
   * spread nodes more evenly around the flash.
   * @return the largest available sector to write to 
   */
  command flashsector *SectorMap.nextLargestIdleSector() {
    int leastPages; 
    int sectorIndex;
    flashsector *largestSector = NULL;
   
    leastPages = FLASH_PAGES_PER_SECTOR;
    
    for(sectorIndex = FLASH_FIRST_BLACKBOOK_SECTOR; sectorIndex <= FLASH_LAST_BLACKBOOK_SECTOR; sectorIndex++) {
      currentSectorIndex++;
      if(currentSectorIndex > FLASH_LAST_BLACKBOOK_SECTOR) {
        currentSectorIndex = FLASH_FIRST_BLACKBOOK_SECTOR;
      }
      
      if(!flashSectors[currentSectorIndex].inUse && flashSectors[currentSectorIndex].writePage != FLASH_PAGES_PER_SECTOR && flashSectors[currentSectorIndex].writePage < leastPages) {
        leastPages = flashSectors[currentSectorIndex].writePage;
        largestSector = &flashSectors[currentSectorIndex];
      }
    }
    
    currentSectorIndex = largestSector->index;
    return largestSector;
  }
  
  /**
   * Find a sector for a new Checkpoint file.
   * @return a sector that is not being written to, that will fit the Checkpoint
   *    file, and that has the least number of unfinalized files.
   */
  command flashsector *SectorMap.nextBestCheckpointSector(uint8_t checkpointPages) {
    uint8_t sectorIndex;
    flashsector *bestSector = NULL;
   
    for(sectorIndex = FLASH_FIRST_BLACKBOOK_SECTOR; sectorIndex <= FLASH_LAST_BLACKBOOK_SECTOR; sectorIndex++) {
      if(!flashSectors[sectorIndex].inUse && (FLASH_PAGES_PER_SECTOR - flashSectors[sectorIndex].writePage) >= checkpointPages) {
        if(flashSectors[sectorIndex].totalUnfinalizedNodes == 0) {
          return &flashSectors[sectorIndex];
            
        } else if(bestSector == NULL) {
          bestSector = &flashSectors[sectorIndex];
            
        } else if(flashSectors[sectorIndex].totalUnfinalizedNodes < bestSector->totalUnfinalizedNodes) {
          bestSector = &flashSectors[sectorIndex];
        }
      }
    }
    
    return bestSector;
  }
  
  /**
   * @return the total nodes in the given sector
   */
  command uint8_t SectorMap.getNodesInSector(flashsector *focusedSector) {
    return focusedSector->totalNodes;
  }
  
  /**
   * Get the flashsector at the specified address in flash.
   * @return the flashsector that exists at the given address
   *     NULL if the flashAddress is out of bounds.
   */
  command flashsector *SectorMap.getSectorAtAddress(uint32_t flashAddress) {
    return &flashSectors[flashAddress / FLASH_SECTOR_LENGTH];
  }
  
  /**
   * Get the sector at a specified volume index
   */
  command flashsector *SectorMap.getSectorAtVolume(uint8_t volume) {
    return &flashSectors[volume];
  }
  
  /**
   * @return TRUE if the sector can be erased
   */
  command bool SectorMap.canErase(uint8_t volume) {
    return flashSectors[volume].totalNodes == 0 && call SectorMap.getSectorWriteAddress(&flashSectors[volume]) != call SectorMap.getSectorBaseAddress(&flashSectors[volume]);
  }
  
  
  /**
   * Finds the sector the given node exists within,
   * then calculates how many pages the node takes up.
   * If the ending flash address of the node is greater
   * than the sector's length, the sector size is adjusted.
   * If the node is currently unfinalized on flash, the state should be
   * NODE_WRITING_UNFINALIZED
   * If the node is freshly finalized on flash, the state should be
   * NODE_IDLE
   */
  command void SectorMap.documentNode(node *focusedNode) {
    flashsector *mySector;
    uint32_t finalNodeAddress;

    mySector = call SectorMap.getSectorAtAddress(focusedNode->flashAddress);

    if(call SectorMap.getSectorWriteAddress(mySector) <= focusedNode->flashAddress) {      
      // Note the address check above - if this node was already registered,
      // then totalUnfinalizedNodes can't be incremented twice.
      // Unfinalized 
      if(focusedNode->state == NODE_WRITING_UNFINALIZED) {
        // Do not change the sector write address for unfinalized writing nodes
        mySector->totalUnfinalizedNodes++;
        return;
        
      } else if(focusedNode->state == NODE_CLOSED_UNFINALIZED) {
        mySector->totalUnfinalizedNodes++;

      } else if(focusedNode->state != NODE_DELETED) {
        mySector->totalNodes++;
      }
    }
    
    if((call NodeMap.getFileFromNode(focusedNode))->firstNode == focusedNode) {
      finalNodeAddress = focusedNode->flashAddress + call Util.getNextPageAddress(focusedNode->dataLength + sizeof(nodemeta) + sizeof(filemeta) - 1);
    } else {
      finalNodeAddress = focusedNode->flashAddress + call Util.getNextPageAddress(focusedNode->dataLength + sizeof(nodemeta) - 1);
    }
    
    if(call SectorMap.getSectorWriteAddress(mySector) < finalNodeAddress) {
      mySector->writePage = call Util.convertBytesToPages(finalNodeAddress - call SectorMap.getSectorBaseAddress(mySector) - 1);
    }
  }
  
  
  /**
   * Document that a node has been finalized to flash
   */
  command void SectorMap.finalizeNode(node *focusedNode) {
    (call SectorMap.getSectorAtAddress(focusedNode->flashAddress))->totalUnfinalizedNodes--;
  }
  
  /**
   * Remove a valid node from its sector. 
   * This helps the garbage collector know which sectors to erase.
   */
  command void SectorMap.removeNode(node *focusedNode) { 
    if(call SectorMap.getSectorWriteAddress(call SectorMap.getSectorAtAddress(focusedNode->flashAddress)) > focusedNode->flashAddress) {      
      (call SectorMap.getSectorAtAddress(focusedNode->flashAddress))->totalNodes--;
    }
  }
  
  /**
   * @return TRUE if the given node is within the bounds of the given sector
   */
  command bool SectorMap.isInSector(flashsector *focusedSector, node *focusedNode) {
    return (call SectorMap.getSectorBaseAddress(focusedSector) <= focusedNode->flashAddress)
        && focusedNode->flashAddress < (call SectorMap.getSectorBaseAddress(focusedSector) + FLASH_SECTOR_LENGTH);
  }
  
  /**
   * Retreive the earliest available write address in a given sector.
   * @return the write address of the sector relative to 0x0
   */
  command uint32_t SectorMap.getSectorWriteAddress(flashsector *focusedSector) {
    return call Util.convertPagesToBytes(focusedSector->writePage) + call SectorMap.getSectorBaseAddress(focusedSector);
  }
  
  /**
   * Retrieve the base address of the flashsector relative to 0x0.
   * @return the relative address of the flashsector from 0x0
   */
  command uint32_t SectorMap.getSectorBaseAddress(flashsector *focusedSector) {
    return focusedSector->index * FLASH_SECTOR_LENGTH;
  }
  
  /**
   * Obtain the remaining free bytes in a specified
   * flashsector
   * @param focusedSector - the flashsector to find the free bytes in
   * @return the number of free page bytes in the flashsector
   */
  command uint32_t SectorMap.bytesRemaining(flashsector *focusedSector) {
    if(focusedSector->writePage < FLASH_PAGES_PER_SECTOR) {
      return FLASH_SECTOR_LENGTH - call Util.convertPagesToBytes(focusedSector->writePage);
    } else {
      return 0;
    }
  }

  /**
   * Obtain the total amount of free space on the flash.
   * @return the total amount of free space on the flash
   */
  command uint32_t SectorMap.getFreeSpace() {
    uint8_t sectorIndex;
    uint32_t totalSpace;
    
    sectorIndex = 0;
    totalSpace = 0;

    // This performs a size estimate of the biggest single file we can create on flash.
    for(sectorIndex = 0; sectorIndex < FLASH_TOTAL_SECTORS; sectorIndex++) {
      if(!flashSectors[sectorIndex].inUse && flashSectors[sectorIndex].writePage < FLASH_PAGES_PER_SECTOR) {
        totalSpace += call SectorMap.bytesRemaining(&flashSectors[sectorIndex]);
        totalSpace -= sizeof(nodemeta);
      }
    }
    
    if(totalSpace > 0) {
      totalSpace -= sizeof(filemeta);
    }
    
    return totalSpace;
  }
  
  /**
   * Notify the Sector module that a write file
   * has been closed
   * @return SUCCESS if the changes were documented
   */
  command void SectorMap.free(node *closedNode) {
    (call SectorMap.getSectorAtAddress(closedNode->flashAddress))->inUse = FALSE;
  }
  
  /**
   * Tell the Sector module that a write file
   * is being opened at the given flashsector
   * @return SUCCESS if the changes were documented
   */
  command void SectorMap.reserve(node *openNode) {
    (call SectorMap.getSectorAtAddress(openNode->flashAddress))->inUse = TRUE;
  }
  
  /**
   * @return TRUE if the sector at the given index is in use
   */
  command bool SectorMap.isInUse(uint8_t sectorIndex) {
    return flashSectors[sectorIndex].inUse;
  }
  
  /**
   * @param sectorIndex - the index of the sector to set inUse
   * @param inUse - TRUE if this sector is in use, FALSE if it isn't
   */
  command void SectorMap.setInUse(uint8_t sectorIndex, bool inUse) {
    flashSectors[sectorIndex].inUse = inUse;
  }
  
  /**
   * The sector was erased by the garbage collector
   * @param sectorIndex - the sector erased
   */
  command void SectorMap.eraseComplete(uint8_t sectorIndex) {
    flashSectors[sectorIndex].totalUnfinalizedNodes = 0;
    flashSectors[sectorIndex].writePage = 0;
    flashSectors[sectorIndex].totalNodes = 0;
    flashSectors[sectorIndex].inUse = FALSE;
  } 
}


--- NEW FILE: WriteAllocM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook File Write Allocator
 * This component allows you to open a file with the given name and minimum
 * size, and the given type for writing.  If the file already exists,
 * it will be opened and if needed, more space will be allocated to the file
 * to meet the new minimum space requirements.  If the file doesn't exist,
 * it will be allocated in both the NodeMap and on flash and created.
 * In the end, if enough space exists and the file is created successfully,
 * the node that can be written to in the file will be passed back.
 * When whatever component fills that node completely and wants to keep
 * writing, it will need to load up the next node in the file if it exists.
 * @author David Moss - dmm at rincon.com
 */
 
module WriteAllocM {
  provides {
    interface WriteAlloc;
    interface StdControl;
  }
  
  uses {
    interface SectorMap;
    interface NodeMap;
    interface NodeBooter;
    interface NodeShop;
    interface State;
    interface BClean;
    interface Util;
  }
}

implementation {

  /** The file we're trying to open for writing */
  file *currentFile;
  
  /** The node we're working on */
  node *currentNode;
  
  /** Previous node in the file to the newly allocated current node */
  node *lastNode;
  
  /** The current sector we're looking at */
  flashsector *currentSector;
  
  /** The minimum size to create the file */
  uint32_t minSize;
  
  /** The total size to create the file */
  uint32_t totalSize;
  
  /** Result to finish with */
  result_t finishResult;
  
  /** Current index in the NodeMap we're looking to finalize */
  uint16_t finalizeNodeIndex;
  
  enum {
    S_IDLE = 0,
    S_OPEN,
    S_CHECKPOINT_FINALIZE,
  };

  /***************** Prototypes ****************/
  /** Set the 'finishResult' variable and call finish to signal completion */
  task void finish();
  
  /** Allocate space on flash for the currentNode */
  task void allocate();
  
  /** Finalize all the nodes in a given sector */
  task void sectorFinalizationLoop();
  
  
  /** Allocate and test a single sector to the currentNode */
  result_t allocateOneSector();
  
  /** After the allocated sector has no writing nodes, we can setup the new node */
  void handleAllocatedSector();
  
  /** Process the newly allocated sector for the current binary node */
  void processNewBinarySector();
  
  /** Start the process of finalizing a sector */
  void startFinalizingSector();
  
  /** Function that deconstructs the file, sets the finishResult to fail, and completes */
  void fail();
  
  /** Deconstruct the current file */
  void closeCurrentFile();
 
  /** Find and unfinalize (if necessary) the first writable node of a file */
  void getFirstWritableNode(); 
  
  /** Get the best sector for the current file type */
  flashsector *requestSector();
  
  /** FALSE if we will not finalize the new dictionary to flash because a version already exists */
  bool forceDictionary;
  
  /***************** StdControl commands ****************/
  command result_t StdControl.init() {
    return SUCCESS;
  }
  
  command result_t StdControl.start() {
    return SUCCESS;
  }
  
  command result_t StdControl.stop() {
    return SUCCESS;
  }
  
  /***************** WriteAlloc Commands ****************/
  /**
   * Open a file for writing
   * Create a file with the given name and the specified minimum length
   * Recipes:
   *   TYPE_BINARY
   *     1. Retrieve the file. If it doesn't exist, allocate a virtual file.
   *     2. Retrieve the first writable node - if it needs to be allocated,
   *        its state becomes NODE_TEMPORARY.  If it already exists and the
   *        total file's minimum requested length is <= than the reserveLength,
   *        change its state and return the node.
   *     3. FlashAlloc space for the node, reserve sector space.
   *     4. If there isn't enough space, repeat step 2 and/or garbage collect.
   *     5. If not enough nodes or space can be allocated, deconstruct file
   *        an FAIL.
   *     6. If enough space is allocated, unfinalize the first node of the file.
   *     7. After unfinalizing, return the first node for writing.
   *
   *   TYPE_KEYVALUE
   *     1. Retrieve one file
   *     2. Retrieve a node, its state becomes NODE_TEMPORARY
   *     3. FlashAlloc space for the node. Do not reserve the sector.
   *     4. If the space allocated is greater than the minimum space,
   *        continue to unfinalize the node.  Else, garbage collect. If
   *        gc fails, deallocate the node and fail.
   *     5. After unfinalizing, finalize the node with the dataLength
   *        set to the size requested to the next page boundary.
   *     6. Set the node's state after finalization.
   *     7. Return the node.
   *
   *   TYPE_CHECKPOINT
   *     1. Retrieve one file
   *     2. Retrieve a node, its state becomes NODE_TEMPORARY
   *     3. FlashAlloc space for the node. Do not reserve the sector.
   *     4. If space is allocated, unfinalize the node. Else, garbage collect.
   *        If gc fails, deallocate the node and fail.
   *     6. Finalize the node after unfinalizing. The dataLength and
   *        reserveLength is set to the size of a checkpoint file.
   *     7. Return the node. It is critical the checkpoint node
   *        is filled out immediately to prevent fault tolerance issues.
   *
   * @param name - the name of the file to open
   * @param minimumSize - the minimum size to create the file
   * @param type - the type of file, defined in Blackbook.h
   * @param forceNewDictionary - TRUE for the special case where we are
   *     creating a new dictionary to replace an existing, open dictionary.
   *     The correct filenameCrc will be created, but the filename in NodeMap
   *     and on flash will be 0xFF's to prevent conflicts with the currently
   *     open file.  Also, the magicNumber on flash will be the constructing
   *     type magic number for the file type given - either checkpoint or keyvalue.
   * @return SUCCESS if the file will be opened for writing.
   */
  command result_t WriteAlloc.openForWriting(filename *name, uint32_t minimumSize, uint8_t type, bool forceNewDictionary) {
    if(!call State.requestState(S_OPEN)) {
      return FAIL;
    }
    
    if(type == TYPE_BINARY) {
      // Binary files cannot be created with force.
      forceNewDictionary = FALSE;
    }
    
    // Do not finalize dictionaries that are being recreated to drop the
    // invalid entries until all valid key-value pairs have been copied 
    // from the original to the new dictionary file:
    forceDictionary = forceNewDictionary;
    minSize = minimumSize;
    totalSize = 0;
    currentFile = call NodeMap.getFile(name);
    
    if(currentFile == NULL || forceNewDictionary) {
      // The file does not exist and needs to be created
      if((currentFile = call NodeBooter.requestAddFile()) == NULL) {
        call State.toIdle();
        return FAIL;
      }
      
      if((currentNode = call NodeBooter.requestAddNode()) == NULL) {
        call State.toIdle();
        return FAIL;
      }
      
      call Util.filenameCpy(&currentFile->name, name->getName);
      currentFile->type = type;
      currentFile->state = FILE_TEMPORARY;
      currentFile->filenameCrc = call Util.filenameCrc(name);
      currentFile->firstNode = currentNode;

      currentNode->filenameCrc = currentFile->filenameCrc;
      currentNode->state = NODE_TEMPORARY;
      
      if(forceNewDictionary) {
        memset(&currentFile->name.getName, 0xFF, sizeof(filename));
      }
      
      // Define the dataLength and reserveLength's for checkpoint and keyvalue files.
      if(currentFile->type == TYPE_CHECKPOINT) {
        currentNode->reserveLength = call Util.convertPagesToBytes(CHECKPOINT_DEDICATED_PAGES) - sizeof(filemeta) - sizeof(nodemeta);

      } else if(currentFile->type == TYPE_KEYVALUE) {
        currentNode->reserveLength = call Util.convertPagesToBytes(call Util.convertBytesToPages(minSize)) - sizeof(filemeta) - sizeof(nodemeta);
      }
      
      post allocate();
      return SUCCESS;
      
    } else {
      // The file already exists.
      if(currentFile->state != FILE_IDLE) {
        call State.toIdle();
        return FAIL;
      }

      currentFile->state = FILE_WRITING;
      currentNode = currentFile->firstNode;

      if(currentFile->type == TYPE_KEYVALUE || currentFile->type == TYPE_CHECKPOINT) {
        currentNode->state = NODE_OPEN;
        finishResult = SUCCESS;
        post finish();
        return SUCCESS;
        
      } else if(currentFile->type == TYPE_BINARY) {
        
        // Traverse through each existing node of the file.
        do {  
          if(currentNode->state == NODE_WRITING_UNFINALIZED) {
            // When reopening an unfinalized open node, reset the reserved length and reserve the sector.
            currentNode->reserveLength = call Util.getNextSectorAddress(currentNode->flashAddress) - sizeof(nodemeta) - currentNode->flashAddress;
            if(currentFile->firstNode == currentNode) {
              currentNode->reserveLength -= sizeof(filemeta);
            }
            call SectorMap.reserve(currentNode);
            
          } else if(currentNode->state == NODE_CLOSED_UNFINALIZED) {
            // since the file was previously closed,
            // its reserveLength cannot take up the entire node.
            // so the sector does not need to be reserved.
            currentNode->state = NODE_WRITING_UNFINALIZED;
          }
          
          totalSize += currentNode->reserveLength;
          lastNode = currentNode;
        } while((currentNode = currentNode->nextNode) != NULL);
        
        // 'lastNode' now contains the last node of the file.
        // 'currentNode' now contains NULL
        
        if(totalSize < minSize || currentNode->state == NODE_IDLE) {
          // Allocate more nodes to this file.
          if((currentNode = call NodeBooter.requestAddNode()) == NULL) {
            // Can't - we're out of nodes in our NodeMap.
            closeCurrentFile();
            return FAIL;
            
          } else {
            lastNode->nextNode = currentNode;
            currentNode->filenameCrc = currentFile->filenameCrc;
            currentNode->state = NODE_TEMPORARY;
            post allocate();
            return SUCCESS;
          }
          
        } else {
          // Enough reserve space exists already, hand it over.
          currentNode = lastNode;
          finishResult = SUCCESS;
          post finish();
          return SUCCESS;
          
        }
      } 
    }
    
    return SUCCESS;
  }
  
  /***************** NodeShop Events ****************/  
  
  /**
   * Unfinalized metadata was written to flash.
   * @param focusedNode - the node that metadata was written for.
   * @param focusedFile - the file that metadata was written for.
   * @param dataStartAddress - the address where data can start being written.
   * @param result - SUCCESS if the metadata was correctly written.
   */
  event void NodeShop.unfinalizedMetaWritten(node *focusedNode, file *focusedFile, uint32_t dataStartAddress, result_t result) {
    if(currentFile->type == TYPE_BINARY) {
      // Binary file type - ready to go
      finishResult = SUCCESS;
      post finish();

    } else {
      // Dictionary and Checkpoint file types - possibly need finalization
      if(!forceDictionary) {
        call NodeShop.writeFinalizedNode(currentNode, currentFile);
          
      } else {
        currentNode->state = NODE_OPEN;
        finishResult = SUCCESS;
        post finish();
      }
    }
  }
  
  /**
   * Finalized metadata was written to flash. This node
   * can no longer be written to.
   * @param focusedNode - the node that metadata was finalized for
   * @param result - SUCCESS if the metadata was correctly finalized.
   */
  event void NodeShop.finalizedMetaWritten(node *focusedNode, result_t result) {
    if(call State.getState() == S_CHECKPOINT_FINALIZE) {
      // Finalizing all the open nodes on the new checkpoint sector
      finalizeNodeIndex++;
      post sectorFinalizationLoop();
      return;
    
    } else {
      // We're opening this node - state is implicit and not checked.
      currentNode->state = NODE_OPEN;
      finishResult = SUCCESS;
      post finish();
    }
  }
  
  /**
   * A node was deleted from flash by marking its magic number
   * invalid in the metadata.
   * @param focusedNode - the node that was deleted.
   * @param result - SUCCESS if the node was deleted successfully.
   */
  event void NodeShop.metaDeleted(node *focusedNode, result_t result) {
  }
 
  /**
   * A crc was calculated from node data on flash
   * @param dataCrc - the crc of the data read from the node on flash.
   * @param result - SUCCESS if the crc is valid
   */
  event void NodeShop.crcCalculated(uint16_t dataCrc, result_t result) {
  }
  
  /**
   * A node was checkpointed and the magic number was
   * updated on flash.
   * @param focusedNode - the node that is now closed and unfinalized on flash
   * @param result - SUCCESS if the node was correctly closed
   */
  event void NodeShop.closedUnfinalizedMetaWritten(node *focusedNode, result_t result) {
    handleAllocatedSector();
  }
  
  
  /***************** BClean Tasks ****************/ 
  /**
   * Garbage Collection is complete
   * @return SUCCESS if any sectors were erased.
   */
  event void BClean.gcDone(result_t result) {
    if(call State.getState() != S_OPEN) {
      // State must be checked here because gc is not parameterized
      return;
    }
    
    if(!result) {
      fail();
      return;
    }
    
    if(!allocateOneSector()) {
      fail();
    }
  }
  
  event void BClean.erasing() {
  }
  
  /***************** Tasks ****************/
  /**
   * A node has already been reserved in the NodeMap,
   * this task will allocate space on flash for the node.
   */
  task void allocate() {
    if(!allocateOneSector()) {
      call BClean.gc();
    }
  }
  
  /**
   * Loop through all nodes existing in the SectorMap.
   * If the node is unfinalized, check to see if it belongs
   * in the currentSector.  If it does, finalize it.
   */
  task void sectorFinalizationLoop() {
    if(finalizeNodeIndex < call NodeMap.getMaxNodes()) {
      if((call NodeMap.getNodeAtIndex(finalizeNodeIndex))->state == NODE_CLOSED_UNFINALIZED) {
        if(call SectorMap.isInSector(currentSector, call NodeMap.getNodeAtIndex(finalizeNodeIndex))) {
          call NodeShop.writeFinalizedNode(call NodeMap.getNodeAtIndex(finalizeNodeIndex), call NodeMap.getFileFromNode(call NodeMap.getNodeAtIndex(finalizeNodeIndex)));
        }
      }
      finalizeNodeIndex++;
      post sectorFinalizationLoop();
      
    } else {
      call State.forceState(S_OPEN);
      call NodeShop.writeUnfinalizedNode(currentFile->firstNode, currentFile);
    }
  }
  
  /**
   * Finish with the result stored in 'finishResult'
   */
  task void finish() {
    call State.toIdle(); 
    signal WriteAlloc.openedForWriting(currentFile, currentNode, finishResult);
  }

  
  /***************** Functions ****************/
  /**
   * This function will allocate one sector for the currentNode
   * and determine if we need to allocate more sectors
   * for binary files.  When enough space is allocated, it finishes
   * up and returns the first writable node.  If it can't find enough
   * space, it returns FAIL, which will either run the garbage collector
   * or stop the allocation process.
   */
  result_t allocateOneSector() {
    int i;
    if((currentSector = requestSector()) == NULL) {
      // No free sectors
      return FAIL;
      
    } else {
      // First verify there are no writing unfinalized nodes in this sector
      for(i = 0; i < call NodeMap.getTotalNodes(); i++) {
        if((call NodeMap.getNodeAtIndex(i))->state == NODE_WRITING_UNFINALIZED) {
          if((call NodeMap.getNodeAtIndex(i))->filenameCrc != currentFile->filenameCrc && call SectorMap.isInSector(currentSector, call NodeMap.getNodeAtIndex(i))) {
            // There can be a max of one writing unfinalized node per sector 
            // We found one, so close this it before making a new node after it.
            call NodeShop.closeUnfinalizedNode(call NodeMap.getNodeAtIndex(i), currentFile);
            return SUCCESS;
          }
        }
      }
    
      // No writing unfinalized nodes found, continue processing this sector
      handleAllocatedSector();
      return SUCCESS;
    }
  }
  
  /**
   * After a sector is allocated and there are no unfinalized writing nodes
   * we can setup the new node
   */
  void handleAllocatedSector() {
    currentNode->flashAddress = call SectorMap.getSectorWriteAddress(currentSector);
    currentNode->dataLength = 0;
    currentNode->dataCrc = 0;
    
    if(currentFile->type == TYPE_BINARY) {
      // Binary file type
      processNewBinarySector();
      return;
      
    } else {
      // Dictionary and Checkpoint file types.
      // Their reserveLengths have been set, but dataLengths have not
      currentNode->dataLength = currentNode->reserveLength;
      
      if(call SectorMap.bytesRemaining(currentSector) < currentNode->reserveLength) {
        // Not enough free space for this node.
        call BClean.gc();
      
      } else {
        if(currentFile->type == TYPE_CHECKPOINT) {
          startFinalizingSector();
        
        } else {
          call NodeShop.writeUnfinalizedNode(currentFile->firstNode, currentFile);
        }
      }
    }
  }
  
  /**
   * After a new sector is allocated to a binary type file, 
   * we need to check to see if we have met the minimum space requirement.
   * If not, continue allocating more nodes and sectors.
   * Otherwise, finish up and signal done with the correct node to write to.
   */
  void processNewBinarySector() {
    call SectorMap.reserve(currentNode);
    currentNode->reserveLength = call SectorMap.bytesRemaining(currentSector) - sizeof(nodemeta);
    if(currentFile->firstNode == currentNode) {
      currentNode->reserveLength -= sizeof(filemeta);
    }
    
    totalSize += currentNode->reserveLength;
        
    if(totalSize < minSize) {
      // Need to allocate more space
      lastNode = currentNode;
      if((currentNode = call NodeBooter.requestAddNode()) == NULL) {
        // We're out of nodes in our NodeMap.
        fail();
      
      } else {
        lastNode->nextNode = currentNode;
        currentNode->filenameCrc = currentFile->filenameCrc;
        currentNode->state = NODE_TEMPORARY;
        post allocate();
      
      }
      
    } else {
      // Enough space is allocated - unfinalize the first temporary node.
      getFirstWritableNode();
    }
  }
  
  
  /**
   * Begin the finalization process for the given sector
   * before writing information about the checkpoint node.
   * This will set the component into a different state,
   * loop through every node in the NodeMap, and finalize
   * the unfinalized ones. After every node in the sector
   * is finalized, the checkpoint can be written.
   */
  void startFinalizingSector() {
    finalizeNodeIndex = 0;
    call State.forceState(S_CHECKPOINT_FINALIZE);
    post sectorFinalizationLoop();
  }
  
  
  /**
   * Find the first writable node of the file. If it needs
   * to be unfinalized, unfinalize it.  Finish up by
   * signaling completion with the first writable unfinalized node.
   * The app will write to the node after the node's dataLength
   * location.
   */
  void getFirstWritableNode() {
    currentNode = currentFile->firstNode;
    
    do {
      // Traverse through each existing node of the file.
      // Find the first writable node.
      if(currentNode->state == NODE_WRITING_UNFINALIZED) {
        finishResult = SUCCESS;
        post finish();
        return;
        
      } else if(currentNode->state == NODE_TEMPORARY) {
        call NodeShop.writeUnfinalizedNode(currentNode, currentFile);
        return;
        
      }
    } while((currentNode = currentNode->nextNode) != NULL);
    
    fail();  // Something bad happened. We should have succeeded.
  }
  
  
  /**
   * This operation failed.
   */
  void fail() {
    closeCurrentFile();
    finishResult = FAIL;
    post finish();
  }
  
  /**
   * Deallocate and close all nodes for the current
   * binary file - this happens before any interaction
   * outside of WriteAlloc so no changes need to be
   * made to flash.
   */
  void closeCurrentFile() {
    if(currentFile->state == FILE_TEMPORARY) {
      currentFile->state = FILE_EMPTY;
      
    } else if(currentFile->state == FILE_WRITING) {
      currentFile->state = FILE_IDLE;
      
    }
    
    currentNode = currentFile->firstNode;
    
    do {
      if(currentNode->state == NODE_TEMPORARY) {
        if(currentNode->reserveLength > 0) {
          call SectorMap.free(currentNode);
        }
        currentNode->state = NODE_EMPTY;

      } else if (currentNode->state == NODE_WRITING_UNFINALIZED) {
        currentNode->state = NODE_CLOSED_UNFINALIZED;
        
      }
    } while((currentNode = currentNode->nextNode) != NULL);
  }
  
  /**
   * Get the best sector for the current file type
   */
  flashsector *requestSector() {
    if(currentFile->type == TYPE_CHECKPOINT) {
      return call SectorMap.nextBestCheckpointSector(CHECKPOINT_DEDICATED_PAGES);
    } else {
      return call SectorMap.nextLargestIdleSector();
    }
  }
  
}


--- NEW FILE: BFileWriteC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook BFileWrite Configuration
 * Open, Write, Save, Close Blackbook files.
 * Use unique("BFileWrite") when connecting to a parameterized
 * interface.
 * @author David Moss - dmm at rincon.com
 */

includes Blackbook;

configuration BFileWriteC {
  provides {
    interface StdControl;
    interface BFileWrite[uint8_t id];
  }
}

implementation {
  components BFileWriteM, DataWriterC, NodeMapC, SectorMapC, NodeShopC, WriteAllocC, CheckpointC, StateC, UtilC;

  StdControl = BFileWriteM;
  StdControl = WriteAllocC;
  StdControl = NodeMapC;
  StdControl = NodeShopC;
  StdControl = SectorMapC;
  StdControl = CheckpointC;
  StdControl = StateC;
  
  BFileWrite = BFileWriteM;
  
  BFileWriteM.DataWriter -> DataWriterC;
  BFileWriteM.WriteAlloc -> WriteAllocC;
  BFileWriteM.BlackbookState -> StateC.State[BLACKBOOK_STATE];
  BFileWriteM.CommandState -> StateC.State[unique("State")];
  BFileWriteM.NodeMap -> NodeMapC;
  BFileWriteM.SectorMap -> SectorMapC;
  BFileWriteM.NodeShop -> NodeShopC.NodeShop[unique("NodeShop")];
  BFileWriteM.Checkpoint -> CheckpointC;
  BFileWriteM.Util -> UtilC;

}



--- NEW FILE: Checkpoint.h ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */



/**
 * It is critical the Checkpoint accesses BDictionaryM
 * with the same parameterized interface number on both
 * the BDictionary interface and the CheckpointDictionary
 * interface.
 * This lets it sneak in the back door to have special
 * priveledges - usually BDictionary is setup to 
 * be accessed by the outside world.  But the outside
 * world can set the BlackbookState and then 
 * indirectly access Checkpoint.  Since we know
 * Checkpoint won't try to interfere, we can 
 * let it skip the BlackbookState check.
 */
enum {
  DICTIONARY_CHECKPOINT = unique("BDictionary"),
};




--- NEW FILE: CheckpointM.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distribution costs.
 *  3. Others are not restricted from copying it or using it except as 
 *      set forward in the licensing agreement.
 *  4. Commented source code of any modifications or additions will be 
 *      made available to Rincon Research on the same terms.
 *  5. This notice will remain intact and displayed prominently.
 * 
 * Copies of the complete licensing agreement may be obtained by contacting 
 * Rincon Research, 101 N. Wilmot, Suite 101, Tucson, AZ 85711.
 * 
 * There is no warranty with this product, either expressed or implied.  
 * Use at your own risk.  Rincon Research is not liable or responsible for 
 * damage or loss incurred or resulting from the use or misuse of this software.
 */

/**
 * Blackbook Checkpoint Module
 * Saves the state of open binary nodes to a Checkpoint
 * dicationary file on flash for catastrophic failure recovery.
 *
 * Use unique("Checkpoint") when connecting to a parameterized interface
 *
 * @author David Moss - dmm at rincon.com
 */
 
module CheckpointM {
  provides {
    interface Checkpoint;
    interface StdControl;
  }
  
  uses {
    interface NodeShop;
    interface CheckpointDictionary;
    interface BDictionary;
    interface State;
    interface SectorMap;
    interface FlashBridge;
    interface Util;
    interface EmptyCheck;
  }
}

implementation {

  /** Buffer for a checkpoint filename and empty data check */
  uint8_t dataBuffer[sizeof(filename)];
  
  /** The total amount of bytes verified for an open unfinalized file */
  uint32_t verifyAddress;

  /** TRUE if we currently have a checkpoint file open for interaction */
  bool checkpointFileOpened;
  
  /** The current checkpoint information being read or written to flash */
  checkpoint currentCheckpoint;
  
  /** The current node we're working with to recover */
  node *currentNode;
  
  /** Node meta to write to flash to delete nodes */
  nodemeta currentMeta;
  
  /** The current sector we're recovering in */
  flashsector *currentSector;
  
  /** Checkpoint States */
  enum {
    S_IDLE = 0,
    S_OPEN,
    S_UPDATE,
    S_RECOVER,
    S_DELETE_ONEPAGE,
    S_RECOVERY_VERIFY,
    S_FULL_VERIFY,
    S_NEXTPAGE_VERIFY,
  };
  
  /***************** Prototypes ****************/
  /** Verify all bytes in the last recovered point in a node are blank */
  task void verifyRecoveryPoint();
  
  /** Delete the current page on flash */
  task void deleteOnePage();
  
  /** Perform a full sector verification, marking off all unclean pages */
  task void fullVerify();
  
  /** Mark the attempted recover node as deleted */
  void markNodeDeleted();
  
  /***************** StdControl ****************/
  command result_t StdControl.init() {
    checkpointFileOpened = FALSE;
    memset(&currentMeta, 0xFF, sizeof(nodemeta));
    return SUCCESS;
  }
  
  command result_t StdControl.start() {
    return SUCCESS;
  }
  
  command result_t StdControl.stop() {
    return SUCCESS;
  }
  
  /***************** Checkpoint Commands ****************/
  /**
   * After boot is complete, open the checkpoint file
   * in the BDictionary
   * @return SUCCESS if the checkpoint file will be 
   *     created and/or opened.
   */
  command result_t Checkpoint.openCheckpoint() {
    if(!checkpointFileOpened) {
      if(!call State.requestState(S_OPEN)) {
        return FAIL;
      }
      
      call CheckpointDictionary.openCheckpoint((filename *) "chkpoint.bb_");
      
    } else {
      signal Checkpoint.checkpointOpened(SUCCESS);
    }

    return SUCCESS;
  }
  
  /**
   * Update a node.
   *  > If the node is finalized or deleted, any existing
   *    key will be deleted from the Checkpoint file.
   *  > If the node is unfinalized, its current state 
   *    will be saved in the checkpoint file
   * @param focusedNode - the node to save or delete
   * @return SUCCESS if the information will be updated
   */
  command result_t Checkpoint.update(node *focusedNode) {
    if(!call State.requestState(S_UPDATE)) {
      return FAIL;
    }
    
    currentNode = focusedNode;
    
    if(focusedNode->state == NODE_WRITING_UNFINALIZED || focusedNode->state == NODE_CLOSED_UNFINALIZED) {
      // This node needs to be saved.
      currentCheckpoint.filenameCrc = focusedNode->filenameCrc;
      currentCheckpoint.dataCrc = focusedNode->dataCrc;
      currentCheckpoint.dataLength = focusedNode->dataLength;
      call BDictionary.insert(focusedNode->flashAddress, &currentCheckpoint, sizeof(checkpoint));
      
    } else if(focusedNode->state == NODE_IDLE) {
      // This node should be removed from the Checkpoint.
      call BDictionary.remove(focusedNode->flashAddress);
            
    } else {
      // Nothing to do. Signal and complete.
      signal Checkpoint.updated(currentNode, SUCCESS);
      call State.toIdle();
    }
    
    return SUCCESS;
  }
  
  /**
   * Recover a node.  The boot process will
   * fill the node with as much information as it
   * can determine from flash, including the node's
   * flashAddress, filenameCrc, etc.  Recovery will
   * fill in the extra details of the node, including
   * the node's dataLength, dataCrc, and reserveLength.
   * The reserveLength of a recovered node will always
   * be up to the next page boundary.
   *
   * Because it could have been possible for a node
   * contain data past the recovery point, the 
   * Checkpoint recovery mechanism will verify that
   * the rest of the page of flash after the recovery
   * point is empty (0xFF's).  If it is found not to be
   * empty, no more data is allowed to be written
   * to the page because it will be corrupted.
   * Instead, the node will be finalized to flash.
   * Any attempt to append data to that recovered file
   * will result in a new node being created.
   * 
   * Make sure the node's state is setup correctly.
   * Obviously we're here because the node was unfinalized
   * on flash, but its state should be either NODE_WRITING_UNFINALIZED
   * which corresponds to a magic number META_WRITING_UNFINALIZED,
   * or NODE_CLOSED_UNFINALIZED which corresponds to magic 
   * META_CLOSED_UNFINALIZED.
   *
   * @param focusedNode - the node to recover
   * @return SUCCESS if recovery will proceed
   */
  command result_t Checkpoint.recover(node *focusedNode) {
    if(!call State.requestState(S_RECOVER)) {
      return FAIL;
    }
    
    currentNode = focusedNode;
    currentSector = call SectorMap.getSectorAtAddress(currentNode->flashAddress);
    
    if(!call BDictionary.retrieve(currentNode->flashAddress, &currentCheckpoint, sizeof(currentCheckpoint))) {
      verifyAddress = currentNode->flashAddress;
      markNodeDeleted();
      post deleteOnePage();
    }
    
    return SUCCESS;
  }
  
  /***************** BDictionary Events ****************/
  
  /**
   * A Dictionary file was opened successfully.
   * @param totalSize - the total amount of flash space dedicated to storing
   *     key-value pairs in the file
   * @param remainingBytes - the remaining amount of space left to write to
   * @param result - SUCCESS if the file was successfully opened.
   */
  event void BDictionary.opened(uint16_t totalSize, uint16_t remainingBytes, result_t result) {
    call State.toIdle();
    signal Checkpoint.checkpointOpened(result);
  }
  
  /** 
   * The opened Dictionary file is now closed
   * @param result - SUCCSESS if there are no open files
   */
  event void BDictionary.closed(result_t result) {
    checkpointFileOpened = FALSE;
  }
  
  /**
   * A key-value pair was inserted into the currently opened Dictionary file.
   * @param key - the key used to insert the value
   * @param value - pointer to the buffer containing the value.
   * @param valueSize - the amount of bytes copied from the buffer into flash
   * @param result - SUCCESS if the key was written successfully.
   */
  event void BDictionary.inserted(uint32_t key, void *value, uint16_t valueSize, result_t result) {
    call State.toIdle();
    signal Checkpoint.updated(currentNode, result);
  }
  
  /**
   * A value was retrieved from the given key.
   * @param key - the key used to find the value
   * @param valueHolder - pointer to the buffer where the value was stored
   * @param valueSize - the actual size of the value.
   * @param result - SUCCESS if the value was pulled out and is uncorrupted
   */
  event void BDictionary.retrieved(uint32_t key, void *valueHolder, uint16_t valueSize, result_t result) {
    if(result) {
      if(currentNode->filenameCrc == currentCheckpoint.filenameCrc) {
        currentNode->dataLength = currentCheckpoint.dataLength;
        currentNode->reserveLength = call Util.getNextPageAddress(currentNode->flashAddress + currentNode->dataLength) - (currentNode->flashAddress + sizeof(nodemeta));
        if(currentNode->fileElement == 0) {
          currentNode->reserveLength -= sizeof(filemeta);
        }
        currentNode->dataCrc = currentCheckpoint.dataCrc;
        post verifyRecoveryPoint();
        return;
      }
    }
      
    // Everything failed, this node is toast.
    verifyAddress = currentNode->flashAddress;
    currentNode->state = NODE_DELETED;
    post deleteOnePage();
  }
  
  /**
   * A key-value pair was removed
   * @param key - the key that should no longer exist
   * @param result - SUCCESS if the key was really removed
   */
  event void BDictionary.removed(uint32_t key, result_t result) {
    if(call State.getState() == S_UPDATE) {
      signal Checkpoint.updated(currentNode, result);
      call State.toIdle();
    
    } else if(call State.getState() == S_FULL_VERIFY) {
      // After a recovered node is finalized, its checkpoint is erased
      verifyAddress = call Util.getNextPageAddress(currentNode->flashAddress + currentNode->dataLength);
      call State.forceState(S_FULL_VERIFY);
      post fullVerify();
    }
  }
  
  /**
   * The next key in the open Dictionary file
   * @param key - the next key
   * @param result - SUCCESS if this information is valid
   */
  event void BDictionary.nextKey(uint32_t nextKey, result_t result) {
  }
  
  
  /***************** NodeShop Events ****************/
  /**
   * Unfinalized metadata was written to flash.
   * @param focusedNode - the node that metadata was written for.
   * @param focusedFile - the file that metadata was written for.
   * @param dataStartAddress - the address where data can start being written.
   * @param result - SUCCESS if the metadata was correctly written.
   */
  event void NodeShop.unfinalizedMetaWritten(node *focusedNode, file *focusedFile, uint32_t dataStartAddress, result_t result) {
  }
  
  /**
   * Finalized metadata was written to flash. This node
   * can no longer be written to.
   * @param focusedNode - the node that metadata was finalized for
   * @param result - SUCCESS if the metadata was correctly finalized.
   */
  event void NodeShop.finalizedMetaWritten(node *focusedNode, result_t result) {
    // Here we're in a middle of an S_FULL_VERIFY
    // This happens when the node was open before the file system stopped
    // and data exists after the recovery point in the last page of the node
    call BDictionary.remove(currentNode->flashAddress);
  }
  
  /**
   * A node was deleted from flash by marking its magic number
   * invalid in the metadata.
   * @param focusedNode - the node that was deleted.
   * @param result - SUCCESS if the node was deleted successfully.
   */
  event void NodeShop.metaDeleted(node *focusedNode, result_t result) {

  }
 
  /**
   * A crc was calculated from node data on flash
   * @param dataCrc - the crc of the data read from the node on flash.
   * @param result - SUCCESS if the crc is valid
   */
  event void NodeShop.crcCalculated(uint16_t dataCrc, result_t result) {
  }
  
  /**
   * A node was checkpointed and the magic number was
   * updated on flash.
   * @param focusedNode - the node that is now closed and unfinalized on flash
   * @param result - SUCCESS if the node was correctly closed
   */
  event void NodeShop.closedUnfinalizedMetaWritten(node *focusedNode, result_t result) {
    // Here we're in the start or middle of an S_FULL_VERIFY
    verifyAddress = call Util.getNextPageAddress(currentNode->flashAddress + currentNode->dataLength);
    call State.forceState(S_FULL_VERIFY);
    post fullVerify();
  }
  
  /***************** FlashBridge Events ****************/
  /**
   * Read is complete
   * @param addr - the address to read from
   * @param *buf - the buffer to read into
   * @param len - the amount to read
   * @return SUCCESS if the bytes will be read
   */
  event void FlashBridge.readDone(uint32_t addr, void *buf, uint32_t len, result_t result) {
  }
  
  /**
   * Write is complete
   * @param addr - the address to write to
   * @param *buf - the buffer to write from
   * @param len - the amount to write
   * @return SUCCESS if the bytes will be written
   */
  event void FlashBridge.writeDone(uint32_t addr, void *buf, uint32_t len, result_t result) {
    //if(!result) {
    //  post deleteOnePage();
    //  return;
    //}
    
    // Do not document this deleted node, because the sector's node count will fail
    verifyAddress = call Util.getNextPageAddress(verifyAddress);
    call State.forceState(S_FULL_VERIFY);
    post fullVerify();
  }
  
  /**
   * Erase is complete
   * @param sector - the sector id to erase
   * @return SUCCESS if the sector will be erased
   */
  event void FlashBridge.eraseDone(uint16_t sector, result_t result) {
  }
  
  /**
   * Flush is complete
   * @param result - SUCCESS if the flash was flushed
   */
  event void FlashBridge.flushDone(result_t result) {
  }
  
  /**
   * CRC-16 is computed
   * @param crc - the computed CRC.
   * @param addr - the address to start the CRC computation
   * @param len - the amount of data to obtain the CRC for
   * @return SUCCESS if the CRC will be computed.
   */
  event void FlashBridge.crcDone(uint16_t calculatedCrc, uint32_t addr, uint32_t len, result_t result) {
  }

  /**
   * Signaled when the flash is ready to be used
   * @param result - SUCCESS if we can use the flash.
   */
  event void FlashBridge.ready(result_t result) {
  }

  /***************** EmptyCheck Events ****************/
  /**
   * The empty check is complete
   * @param empty - TRUE if the given range on flash is empty
   * @param result - SUCCESS if the result is valid
   */
  event void EmptyCheck.emptyCheckDone(bool empty, result_t result) {
    if(call State.getState() == S_RECOVERY_VERIFY) {
      // Here we know the node has been recovered, but we verify no
      // data exists in its last page past the recovery point
      if(!result) {
        post verifyRecoveryPoint();
      }

      if(empty) {
        // The rest of the the last page of this node is ok to write to.
        if(currentNode->state == NODE_WRITING_UNFINALIZED) {
          // Do not close this open node unless we have to,
          // verify the next page is empty as well.
          verifyAddress = call Util.getNextPageAddress(currentNode->flashAddress + currentNode->dataLength);
          call State.forceState(S_NEXTPAGE_VERIFY);
          post fullVerify();
          
        } else {
          // The node was closed before the file system shut down,
          // And because data exists after the recovery point on the last
          // page, this node was succesfully recovered.
          call State.toIdle();
          signal Checkpoint.recovered(SUCCESS);
          return;
        }
        
      } else {
        // Data exists in the last page of the node; this node must be finalized
        currentNode->state = NODE_WRITING_UNFINALIZED;
        call SectorMap.documentNode(currentNode);
        call State.forceState(S_FULL_VERIFY);
        call NodeShop.writeFinalizedNode(currentNode, NULL);
      }
      
      
    } else if(call State.getState() == S_NEXTPAGE_VERIFY) {
      currentNode->state = NODE_WRITING_UNFINALIZED;
      call SectorMap.documentNode(currentNode);
      
      if(empty) {
        // The next page after the last page of the open node is empty
        // Leave this node open.
        call State.toIdle();
        signal Checkpoint.recovered(SUCCESS);
        return;
        
      } else {
        // The next page after the last page of the open node has data.
        // Finalize the node and perform a full verify.
        currentNode->state = NODE_WRITING_UNFINALIZED;
        call SectorMap.documentNode(currentNode);
        call State.forceState(S_FULL_VERIFY);
        call NodeShop.closeUnfinalizedNode(currentNode, NULL);
      }
    
    } else if(call State.getState() == S_FULL_VERIFY) {
      if(empty) {
        // One page was found empty, so we can be pretty sure
        // the rest of the sector is empty unless a page worth of
        // 0xFF's were written, followed by some other byte
        // on a future page. 
        call State.toIdle();
        signal Checkpoint.recovered(SUCCESS);
        
      } else {
        post deleteOnePage();
      }
    }
  }
 
 
  /***************** Tasks ****************/
  /**
   * Write metadata to make a deleted node at the
   * current verifyAddress.
   *
   * We use a fileElement of 0 because if the
   * page we're deleting contains nodemeta, we'll
   * have the correct dataLength 
   */
  task void deleteOnePage() {
    call State.forceState(S_DELETE_ONEPAGE);

    currentMeta.dataLength = FLASH_PAGE_LENGTH - sizeof(nodemeta) - sizeof(filemeta);
    currentMeta.fileElement = 0;
    currentMeta.magicNumber = META_INVALID;
    
    if(!call FlashBridge.write(verifyAddress, &currentMeta, sizeof(currentMeta))) {
      post deleteOnePage();
    }
  }
  
  
  /**
   * Entry point to perform a quick corruption check
   * This is only valid for files that were marked
   * as being closed on flash.  We know if the file
   * is closed that its reserveLength cannot extend
   * past the next page boundary after its dataLength.
   */
  task void verifyRecoveryPoint() {
    uint32_t startAddress = currentNode->flashAddress + sizeof(nodemeta) + currentNode->dataLength;
    startAddress += (currentNode->fileElement == 0) ? sizeof(filemeta) : 0;
    
    call State.forceState(S_RECOVERY_VERIFY);    
    call EmptyCheck.checkEmpty(startAddress, currentNode->reserveLength - currentNode->dataLength);
  }
  
  /** 
   * Verify the page of flash starting at the verifyAddress is
   * completely empty.  If it's not empty, write nodemeta
   * to the beginning of the page to mark it as deleted for
   * the length of a page.
   *
   * Perform the same process on subsequent pages until the
   * end of the current sector is reached.
   *
   * For most cases, ensure the state of this component 
   * is placed in S_FULL_VERIFY before entering this task.
   *
   * @param startOffset - if we recovered any of the file
   *     we can pass this in as a starting point. Otherwise,
   *     set it to 0.
   */
  task void fullVerify() {
    if(verifyAddress < call SectorMap.getSectorBaseAddress(currentSector) + FLASH_SECTOR_LENGTH) {
      if(!call EmptyCheck.checkEmpty(verifyAddress, FLASH_PAGE_LENGTH)) {
        post fullVerify();
      }
      
    } else {
      call State.toIdle();
      signal Checkpoint.recovered(SUCCESS);
    }
  }
  
  /***************** Functions ****************/
  /**
   * This is in a function because it is referenced in two
   * different locations in the recovery process when the node
   * cannot be recovered.
   * Although it's marked as deleted here, it is not the
   * Checkpoint's responsibility to update the SectorMap's
   * write address.
   */
  void markNodeDeleted() {
    currentNode->state = NODE_DELETED;
    currentNode->dataLength = FLASH_PAGE_LENGTH - sizeof(nodemeta) - sizeof(filemeta);
    currentNode->reserveLength = currentNode->dataLength;
    currentNode->fileElement = 0;
  }
  
}



--- NEW FILE: EmptyCheckC.nc ---
/*
 * Copyright (c) 2004-2006 Rincon Research Corporation.  
 * All rights reserved.
 * 
 * Rincon Research will permit distribution and use by others subject to
 * the restrictions of a licensing agreement which contains (among other things)
 * the following restrictions:
 * 
 *  1. No credit will be taken for the Work of others.
 *  2. It will not be resold for a price in excess of reproduction and 
 *      distr