]> oss.titaniummirror.com Git - tinyos-2.x.git/blobdiff - tos/chips/stm25p/Stm25pLogP.nc
Merge devel code into the trunk.
[tinyos-2.x.git] / tos / chips / stm25p / Stm25pLogP.nc
diff --git a/tos/chips/stm25p/Stm25pLogP.nc b/tos/chips/stm25p/Stm25pLogP.nc
new file mode 100644 (file)
index 0000000..7ccde10
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2005-2006 Arch Rock Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the
+ *   distribution.
+ * - Neither the name of the Arch Rock Corporation nor the names of
+ *   its contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
+ * ARCHED ROCK OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE
+ */
+
+/**
+ * @author Jonathan Hui <jhui@archrock.com>
+ * @version $Revision$ $Date$
+ */
+
+#include <Stm25p.h>
+
+module Stm25pLogP {
+  
+  provides interface Init;
+  provides interface LogRead as Read[ uint8_t id ];
+  provides interface LogWrite as Write[ uint8_t id ];
+  
+  uses interface Stm25pSector as Sector[ uint8_t id ];
+  uses interface Resource as ClientResource[ uint8_t id ];
+  uses interface Get<bool> as Circular[ uint8_t id ];
+  uses interface Leds;
+
+}
+
+implementation {
+
+  enum {
+    NUM_LOGS = uniqueCount( "Stm25p.Log" ),
+    BLOCK_SIZE = 4096,
+    BLOCK_SIZE_LOG2 = 12,
+    BLOCK_MASK = BLOCK_SIZE - 1,
+    BLOCKS_PER_SECTOR = STM25P_SECTOR_SIZE / BLOCK_SIZE,
+    MAX_RECORD_SIZE = 254,
+    INVALID_HEADER = 0xff,
+  };
+  
+  typedef enum {
+    S_IDLE,
+    S_READ,
+    S_SEEK,
+    S_ERASE,
+    S_APPEND,
+    S_SYNC,
+  } stm25p_log_req_t;
+
+  typedef struct stm25p_log_state_t {
+    storage_cookie_t cookie;
+    void* buf;
+    uint8_t len;
+    stm25p_log_req_t req;
+  } stm25p_log_state_t;
+
+  typedef struct stm25p_log_info_t {
+    stm25p_addr_t read_addr;
+    stm25p_addr_t remaining;
+    stm25p_addr_t write_addr;
+  } stm25p_log_info_t;
+  
+  stm25p_log_state_t m_log_state[ NUM_LOGS ];
+  stm25p_log_state_t m_req;
+  stm25p_log_info_t m_log_info[ NUM_LOGS ];
+  stm25p_addr_t m_addr;
+  uint8_t m_header;
+  uint8_t m_remaining;
+  uint8_t m_len;
+
+  typedef enum {
+    S_SEARCH_BLOCKS,
+    S_SEARCH_RECORDS,
+    S_SEARCH_SEEK,
+    S_HEADER,
+    S_DATA,
+  } stm25p_log_rw_state_t;
+
+  stm25p_log_rw_state_t m_rw_state;
+
+  error_t newRequest( uint8_t client );
+  void continueReadOp( uint8_t client );
+  void continueAppendOp( uint8_t client );
+  void signalDone( uint8_t id, error_t error );
+  
+  command error_t Init.init() {
+    int i;
+    for ( i = 0; i < NUM_LOGS; i++ ) {
+      m_log_info[ i ].read_addr = STM25P_INVALID_ADDRESS;
+      m_log_info[ i ].write_addr = 0;
+    }
+    return SUCCESS;
+  }
+  
+  command error_t Read.read[ uint8_t id ]( void* buf, storage_len_t len ) {
+    
+    m_req.req = S_READ;
+    m_req.buf = buf;
+    m_req.len = len;
+    m_len = len;
+    return newRequest( id );
+    
+  }
+  
+  command error_t Read.seek[ uint8_t id ]( storage_addr_t cookie ) {
+    
+    if ( cookie > m_log_info[ id ].write_addr )
+      return FAIL;
+    
+    m_req.req = S_SEEK;
+    m_req.cookie = cookie;
+    return newRequest( id );
+    
+  }
+  
+  command storage_cookie_t Read.currentOffset[ uint8_t id ]() {
+    return m_log_info[ id ].read_addr;
+  }
+  
+  command storage_cookie_t Read.getSize[ uint8_t id ]() {
+    return ( (storage_len_t)call Sector.getNumSectors[ id ]()
+             << STM25P_SECTOR_SIZE_LOG2 );
+  }
+  
+  command storage_cookie_t Write.currentOffset[ uint8_t id ]() {
+    return m_log_info[ id ].write_addr;
+  }
+  
+  command error_t Write.erase[ uint8_t id ]() {
+    m_req.req = S_ERASE;
+    return newRequest( id );
+  }
+  
+  command error_t Write.append[ uint8_t id ]( void* buf, storage_len_t len ) {
+    
+    uint16_t bytes_left = (uint16_t)m_log_info[ id ].write_addr % BLOCK_SIZE;
+    bytes_left = BLOCK_SIZE - bytes_left;
+    
+    // don't allow appends larger than maximum record size
+    if ( len > MAX_RECORD_SIZE )
+      return ESIZE;
+
+    // move to next block if current block doesn't have enough space
+    if ( sizeof( m_header ) + len > bytes_left )
+      m_log_info[ id ].write_addr += bytes_left;
+    
+    // if log is not circular, make sure it doesn't grow too large
+    if ( !call Circular.get[ id ]() &&
+        ( (uint8_t)(m_log_info[ id ].write_addr >> STM25P_SECTOR_SIZE_LOG2) >=
+          call Sector.getNumSectors[ id ]() ) )
+      return ESIZE;
+
+    m_req.req = S_APPEND;
+    m_req.buf = buf;
+    m_req.len = len;
+
+    return newRequest( id );
+
+  }
+  
+  command error_t Write.sync[ uint8_t id ]() {
+    m_req.req = S_SYNC;
+    return newRequest( id );
+  }
+  
+  error_t newRequest( uint8_t client ) {
+    
+    if ( m_log_state[ client ].req != S_IDLE )
+      return FAIL;
+    
+    call ClientResource.request[ client ]();
+    m_log_state[ client ] = m_req;
+    
+    return SUCCESS;
+    
+  }
+  
+  event void ClientResource.granted[ uint8_t id ]() {
+
+    // log never used, need to find start and end of log
+    if ( m_log_info[ id ].read_addr == STM25P_INVALID_ADDRESS &&
+        m_log_state[ id ].req != S_ERASE ) {
+      m_rw_state = S_SEARCH_BLOCKS;
+      call Sector.read[ id ]( 0, (uint8_t*)&m_addr, sizeof( m_addr ) );
+    }
+    // start and end of log known, do the requested operation
+    else {
+      switch( m_log_state[ id ].req ) {
+      case S_READ:
+       m_rw_state = (m_log_info[ id ].remaining) ? S_DATA : S_HEADER;
+       continueReadOp( id );
+       break;
+      case S_SEEK:
+       {
+         // make sure the cookie is still within the range of valid data
+         uint8_t numSectors = call Sector.getNumSectors[ id ]();
+         uint8_t readSector = 
+           (m_log_state[ id ].cookie >> STM25P_SECTOR_SIZE_LOG2);
+         uint8_t writeSector =
+           ((m_log_info[ id ].write_addr-1)>>STM25P_SECTOR_SIZE_LOG2)+1;
+         // if cookie is overwritten, advance to beginning of log
+         if ( (writeSector - readSector) > numSectors ) {
+           m_log_state[ id ].cookie = 
+             (storage_cookie_t)(writeSector-numSectors)
+               <<STM25P_SECTOR_SIZE_LOG2;
+         }
+         m_log_info[ id ].read_addr = m_log_state[ id ].cookie & BLOCK_MASK;
+         m_log_info[ id ].remaining = 0;
+         m_rw_state = S_SEARCH_SEEK;
+         if ( m_log_info[ id ].read_addr != m_log_state[ id ].cookie )
+           call Sector.read[ id ]( m_log_info[ id ].read_addr, &m_header,
+                                   sizeof( m_header ) );
+         else
+           signalDone( id, SUCCESS );
+       }
+       break;
+      case S_ERASE:
+       call Sector.erase[ id ]( 0, call Sector.getNumSectors[ id ]() );
+       break;
+      case S_APPEND:
+       m_rw_state = S_HEADER;
+       continueAppendOp( id );
+       break;
+      case S_SYNC:
+       signalDone( id, SUCCESS );
+       break;
+      case S_IDLE:
+       break;
+      }
+    }
+    
+  }
+
+  uint8_t calcSector( uint8_t client, stm25p_addr_t addr ) {
+    uint8_t sector = call Sector.getNumSectors[ client ]();
+    return (uint8_t)( addr >> STM25P_SECTOR_SIZE_LOG2 ) % sector;
+  }
+
+  stm25p_addr_t calcAddr( uint8_t client, stm25p_addr_t addr  ) {
+    stm25p_addr_t result = calcSector( client, addr );
+    result <<= STM25P_SECTOR_SIZE_LOG2;
+    result |= addr & STM25P_SECTOR_MASK;
+    return result;
+  }
+
+  void continueReadOp( uint8_t client ) {
+    
+    stm25p_addr_t read_addr = m_log_info[ client ].read_addr;
+    uint8_t* buf;
+    uint8_t len;
+
+    // check if all done
+    if ( m_len == 0 || read_addr >= m_log_info[ client ].write_addr ) {
+      signalDone( client, SUCCESS );
+      return;
+    }
+    
+    buf = &m_header;
+    len = sizeof( len );
+
+    if ( m_rw_state == S_DATA ) {
+      // if header is invalid, move to next block
+      if ( m_header == INVALID_HEADER ) {
+       m_rw_state = S_HEADER;
+       read_addr += BLOCK_SIZE;
+       read_addr &= ~BLOCK_MASK;
+      }
+      else {
+       buf = m_log_state[ client ].buf;
+       // truncate if record is shorter than requested length
+       if ( m_remaining < m_len )
+         m_log_state[ client ].len = m_remaining;
+       len = m_len;
+      }
+    }
+    
+    // if on block boundary
+    if ( !((uint16_t)read_addr & BLOCK_MASK ) )
+      read_addr += sizeof( storage_addr_t );
+    
+    m_log_info[ client ].read_addr = read_addr;
+    call Sector.read[ client ]( calcAddr( client, read_addr ), buf, len );
+    
+  }
+  
+  event void Sector.readDone[ uint8_t id ]( stm25p_addr_t addr, uint8_t* buf,
+                                           stm25p_len_t len, error_t error ) {
+
+    stm25p_log_info_t* log_info = &m_log_info[ id ];
+
+    // searching for the first and last log blocks
+    switch( m_rw_state ) {
+    case S_SEARCH_BLOCKS: 
+      {
+       uint8_t block = addr >> BLOCK_SIZE_LOG2;
+       // record potential starting and ending addresses
+       if ( m_addr != STM25P_INVALID_ADDRESS ) {
+         if ( m_addr < log_info->read_addr )
+           log_info->read_addr = m_addr;
+         if ( m_addr > log_info->write_addr )
+           log_info->write_addr = m_addr;
+       }
+       // move on to next log block
+       if ( (uint16_t)m_addr == 0 &&
+            ++block < (call Sector.getNumSectors[ id ]()*BLOCKS_PER_SECTOR)) {
+         addr += BLOCK_SIZE;
+         call Sector.read[ id ]( addr, (uint8_t*)&m_addr, sizeof( m_addr ) );
+       }
+       // if log is empty, continue operation
+       else if ( log_info->read_addr == STM25P_INVALID_ADDRESS ) {
+         log_info->read_addr = 0;
+         log_info->write_addr = 0;
+         signal ClientResource.granted[ id ]();
+       }
+       // search for last record
+       else {
+         log_info->write_addr += sizeof( m_addr );
+         m_rw_state = S_SEARCH_RECORDS;
+         call Sector.read[ id ]( log_info->write_addr, &m_header,
+                                 sizeof( m_header ) );
+       }
+      }
+      break;
+
+    case S_SEARCH_RECORDS: 
+      {
+       // searching for the last log record to write
+       uint16_t cur_block = log_info->write_addr >> BLOCK_SIZE_LOG2;
+       uint16_t new_block = ( log_info->write_addr + sizeof( m_header ) + 
+                              m_header ) >> BLOCK_SIZE_LOG2;
+       // if header is valid and is on same block, move to next record
+       if ( m_header != INVALID_HEADER && cur_block == new_block ) {
+         log_info->write_addr += sizeof( m_header ) + m_header;
+         call Sector.read[ id ]( log_info->write_addr, &m_header,
+                                 sizeof( m_header ) );
+       }
+       // found last record
+       else {
+         signal ClientResource.granted[ id ]();
+       }
+      }
+      break;
+
+    case S_SEARCH_SEEK:
+      {
+       // searching for last log record to read
+       log_info->read_addr += sizeof( m_header ) + m_header;
+       // if not yet at cookie, keep searching
+       if ( log_info->read_addr < m_log_state->cookie ) {
+         call Sector.read[ id ]( log_info->read_addr, &m_header,
+                                 sizeof( m_header ) );
+       }
+       // at or passed cookie, stop
+       else {
+         log_info->remaining = log_info->read_addr - m_log_state[ id ].cookie;
+         log_info->read_addr = m_log_state[ id ].cookie;
+         signalDone( id, error );
+       }
+      }
+      break;
+
+    case S_HEADER:
+      {
+       // if header is invalid, move to next block
+       if ( m_header == INVALID_HEADER ) {
+         log_info->read_addr += BLOCK_SIZE;
+         log_info->read_addr &= ~BLOCK_MASK;
+       }
+       else {
+         log_info->read_addr += sizeof( m_header );
+         log_info->remaining = m_header;
+         m_rw_state = S_DATA;
+       }
+       continueReadOp( id );
+      }
+      break;
+
+    case S_DATA:
+      {
+       log_info->read_addr += len;
+       log_info->remaining -= len;
+       m_len -= len;
+       m_rw_state = S_HEADER;
+       continueReadOp( id );
+       break;
+      }
+      
+    }
+    
+  }
+
+  void continueAppendOp( uint8_t client ) {
+    
+    stm25p_addr_t write_addr = m_log_info[ client ].write_addr;
+    uint8_t* buf;
+    uint8_t len;
+    
+    if ( !(uint16_t)write_addr ) {
+      call Sector.erase[ client ]( calcSector( client, write_addr ), 1 );
+    }
+    else {
+      if ( m_rw_state == S_HEADER ) {
+       buf = &m_log_state[ client ].len;
+       len = sizeof( m_log_state[ client ].len );
+      }
+      else {
+       buf = m_log_state[ client ].buf;
+       len = m_log_state[ client ].len;
+      }
+      call Sector.write[ client ]( calcAddr( client, write_addr ), buf, len );
+    }
+
+  }
+
+  event void Sector.eraseDone[ uint8_t id ]( uint8_t sector, 
+                                            uint8_t num_sectors,
+                                            error_t error ) {
+    if ( m_log_state[ id ].req == S_ERASE ) {
+      m_log_info[ id ].read_addr = 0;
+      m_log_info[ id ].write_addr = 0;
+      signalDone( id, error );
+    }
+    else {
+      // advance read pointer if write pointer has gone too far ahead
+      // (the log could have cycled around)
+      stm25p_addr_t volume_size = 
+       STM25P_SECTOR_SIZE * ( call Sector.getNumSectors[ id ]() - 1 );
+      if ( m_log_info[ id ].write_addr >= volume_size ) {
+       stm25p_addr_t read_addr = m_log_info[ id ].write_addr - volume_size;
+       if ( m_log_info[ id ].read_addr < read_addr )
+         m_log_info[ id ].read_addr = read_addr;
+      }
+      m_addr = m_log_info[ id ].write_addr;
+      call Sector.write[ id ]( calcAddr( id, m_addr ), (uint8_t*)&m_addr, 
+                             sizeof( m_addr ) );
+    }
+  }
+
+  event void Sector.writeDone[ uint8_t id ]( storage_addr_t addr, 
+                                            uint8_t* buf, 
+                                            storage_len_t len, 
+                                            error_t error ) {
+    m_log_info[ id ].write_addr += len;
+    if ( m_rw_state == S_HEADER ) {
+      if ( len == sizeof( m_header ) )
+       m_rw_state = S_DATA;
+      continueAppendOp( id );
+    }
+    else {
+      signalDone( id, error );
+    }
+  }
+  
+  void signalDone( uint8_t id, error_t error ) {
+    
+    stm25p_log_req_t req = m_log_state[ id ].req;
+    void* buf = m_log_state[ id ].buf;
+    storage_len_t len = m_log_state[ id ].len;
+
+    call ClientResource.release[ id ]();
+    m_log_state[ id ].req = S_IDLE;
+    switch( req ) {
+    case S_IDLE:
+      break;
+    case S_READ:
+      signal Read.readDone[ id ]( buf, len - m_len, error );
+      break;
+    case S_SEEK:
+      signal Read.seekDone[ id ]( error );
+      break;
+    case S_ERASE:
+      signal Write.eraseDone[ id ]( error );
+      break;
+    case S_APPEND:
+      signal Write.appendDone[ id ]( buf, len, error );
+      break;
+    case S_SYNC:
+      signal Write.syncDone[ id ]( error );
+      break;
+    }
+  }
+
+  event void Sector.computeCrcDone[ uint8_t id ]( stm25p_addr_t addr, stm25p_len_t len, uint16_t crc, error_t error ) {}
+
+  default event void Read.readDone[ uint8_t id ]( void* data, storage_len_t len, error_t error ) {}
+  default event void Read.seekDone[ uint8_t id ]( error_t error ) {}
+  default event void Write.eraseDone[ uint8_t id ]( error_t error ) {}
+  default event void Write.appendDone[ uint8_t id ]( void* data, storage_len_t len, error_t error ) {}
+  default event void Write.syncDone[ uint8_t id ]( error_t error ) {}
+
+  default command storage_addr_t Sector.getPhysicalAddress[ uint8_t id ]( storage_addr_t addr ) { return 0xffffffff; }
+  default command uint8_t Sector.getNumSectors[ uint8_t id ]() { return 0; }
+  default command error_t Sector.read[ uint8_t id ]( storage_addr_t addr, uint8_t* buf, storage_len_t len ) { return FAIL; }
+  default command error_t Sector.write[ uint8_t id ]( storage_addr_t addr, uint8_t* buf, storage_len_t len ) { return FAIL; }
+  default command error_t Sector.erase[ uint8_t id ]( uint8_t sector, uint8_t num_sectors ) { return FAIL; }
+  default command error_t Sector.computeCrc[ uint8_t id ]( uint16_t crc, storage_addr_t addr, storage_len_t len ) { return FAIL; }
+  default async command error_t ClientResource.request[ uint8_t id ]() { return FAIL; }
+  default async command void ClientResource.release[ uint8_t id ]() {}
+  default command bool Circular.get[ uint8_t id ]() { return FALSE; }
+  
+}