]> oss.titaniummirror.com Git - tinyos-2.x.git/commitdiff
Added TosThreads support for Mulle.
authorr-studio <r-studio>
Fri, 9 Apr 2010 14:37:35 +0000 (14:37 +0000)
committerr-studio <r-studio>
Fri, 9 Apr 2010 14:37:35 +0000 (14:37 +0000)
From now on the M16c62p code only uses the interrupt stack. The tosthread library would else not work as intended.
Added a fix folder in the Mulle platforms folder. Se README in that folder for more information.
Did a minor fix to the at45db platform specific code regarding the initialization of the chip. The at45db chip is now initialized through the RealMainP software init so that it will be initialized after the mcu.

tos/lib/tosthreads/chips/m16c62p/chip_thread.h [new file with mode: 0644]
tos/lib/tosthreads/platforms/mulle/ActiveMessageC.nc [new file with mode: 0644]
tos/platforms/mulle/fix/At45dbP.nc [new file with mode: 0644]
tos/platforms/mulle/fix/LogStorageP.nc [new file with mode: 0644]
tos/platforms/mulle/fix/README [new file with mode: 0644]

diff --git a/tos/lib/tosthreads/chips/m16c62p/chip_thread.h b/tos/lib/tosthreads/chips/m16c62p/chip_thread.h
new file mode 100644 (file)
index 0000000..4442432
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2009 Communication Group and Eislab at
+ * Lulea University of Technology
+ *
+ * Contact: Laurynas Riliskis, LTU
+ * Mail: laurynas.riliskis@ltu.se
+ * 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 Communication Group at Lulea University of Technology
+ *   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 STANFORD
+ * UNIVERSITY 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.
+ */
+
+/*
+ * Copyright (c) 2008 Stanford University.
+ * 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 Stanford University 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 STANFORD
+ * UNIVERSITY 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.
+ */
+
+/**
+ * This file contains M16c/62p mcu-specific routines for implementing
+ * threads in TinyOS
+ *
+ * @author Henrik Makitaavola <henrik.makitaavola@gmail.com>
+ * @author Kevin Klues <klueska@cs.stanford.edu>
+ */
+
+typedef struct thread_regs {
+  uint16_t r0;
+  uint16_t r1;
+  uint16_t r2;
+  uint16_t r3;
+  uint16_t a0;
+  uint16_t a1;
+  uint16_t fb;
+  uint16_t flg;
+  uint16_t mem0;
+  uint16_t mem2;
+  uint16_t mem4;
+  uint16_t mem6;
+  uint16_t mem8;
+  uint16_t mem10;
+  uint16_t mem12;
+  uint16_t mem14;
+} thread_regs_t;
+
+/**
+ * Memory is addressed by 16 bits on M16c/62p but because
+ * is can be addressed in 8 bit units we typedef the
+ * stack pointer as a uint8_t.
+ */
+typedef uint8_t* stack_ptr_t;
+  
+#define STACK_TOP(stack, size)    \
+  (&(((uint8_t*)stack)[size - 1]))
+  
+//Save stack pointer
+#define SAVE_STACK_PTR(t)                                      \
+  asm volatile ("stc sp, %0": "=r"(t->stack_ptr));
+
+//Save status register
+#define SAVE_STATUS(t)                                         \
+   asm volatile ("stc flg, %0": "=r"(t->regs.flg));
+  
+//Save General Purpose Registers
+#define SAVE_GPR(t)                                                \
+ asm volatile ("mov.w r0, %0 \n\t" : "=r" ((t)->regs.r0) : );   \
+ asm volatile ("mov.w r1, %0 \n\t" : "=r" ((t)->regs.r1) : );   \
+ asm volatile ("mov.w r2, %0 \n\t" : "=r" ((t)->regs.r2) : );   \
+ asm volatile ("mov.w r3, %0 \n\t" : "=r" ((t)->regs.r3) : );   \
+ asm volatile ("mov.w a0, %0 \n\t" : "=r" ((t)->regs.a0) : );   \
+ asm volatile ("mov.w a1, %0 \n\t" : "=r" ((t)->regs.a1) : );   \
+ asm volatile ("stc fb, %0 \n\t" : "=r" ((t)->regs.fb) : ); \
+ asm volatile ("mov.w mem0, %0 \n\t" : "=r" ((t)->regs.mem0) : );  \
+ asm volatile ("mov.w mem2, %0 \n\t" : "=r" ((t)->regs.mem2) : );  \
+ asm volatile ("mov.w mem4, %0 \n\t" : "=r" ((t)->regs.mem4) : );  \
+ asm volatile ("mov.w mem6, %0 \n\t" : "=r" ((t)->regs.mem6) : );  \
+ asm volatile ("mov.w mem8, %0 \n\t" : "=r" ((t)->regs.mem8) : );  \
+ asm volatile ("mov.w mem10, %0 \n\t" : "=r" ((t)->regs.mem10) : );  \
+ asm volatile ("mov.w mem12, %0 \n\t" : "=r" ((t)->regs.mem12) : );  \
+ asm volatile ("mov.w mem14, %0 \n\t" : "=r" ((t)->regs.mem14) : );  
+  
+//Restore stack pointer
+#define RESTORE_STACK_PTR(t)                                            \
+  asm volatile ("ldc %0, sp \n\t" :: "r" ((t)->stack_ptr))
+
+//Restore status register
+#define RESTORE_STATUS(t)                                      \
+  asm volatile ("ldc %0, flg \n\t" :: "r" (t->regs.flg) ); 
+
+//Restore the general purpose registers
+#define RESTORE_GPR(t)                                              \
+ asm volatile ("mov.w %0, r0 \n\t" :: "r" ((t)->regs.r0)  );   \
+ asm volatile ("mov.w %0, r1 \n\t" :: "r" ((t)->regs.r1)  );   \
+ asm volatile ("mov.w %0, r2 \n\t" :: "r" ((t)->regs.r2)  );   \
+ asm volatile ("mov.w %0, r3 \n\t" :: "r" ((t)->regs.r3)  );   \
+ asm volatile ("mov.w %0, a0 \n\t" :: "r" ((t)->regs.a0)  );   \
+ asm volatile ("mov.w %0, a1 \n\t" :: "r" ((t)->regs.a1)  );   \
+ asm volatile ("ldc %0, fb \n\t" :: "r" ((t)->regs.fb)  ); \
+ asm volatile ("mov.w %0, mem0 \n\t" :: "r" ((t)->regs.mem0)  );   \
+ asm volatile ("mov.w %0, mem2 \n\t" :: "r" ((t)->regs.mem2)  );   \
+ asm volatile ("mov.w %0, mem4 \n\t" :: "r" ((t)->regs.mem4)  );   \
+ asm volatile ("mov.w %0, mem6 \n\t" :: "r" ((t)->regs.mem6)  );   \
+ asm volatile ("mov.w %0, mem8 \n\t" :: "r" ((t)->regs.mem8)  );   \
+ asm volatile ("mov.w %0, mem10 \n\t" :: "r" ((t)->regs.mem10)  );   \
+ asm volatile ("mov.w %0, mem12 \n\t" :: "r" ((t)->regs.mem12)  );   \
+ asm volatile ("mov.w %0, mem14 \n\t" :: "r" ((t)->regs.mem14)  );   
+  
+#define SAVE_TCB(t) \
+  SAVE_GPR(t);         \
+  SAVE_STATUS(t);      \
+  SAVE_STACK_PTR(t) 
+  
+#define RESTORE_TCB(t)  \
+  RESTORE_STACK_PTR(t); \
+  RESTORE_STATUS(t);    \
+  RESTORE_GPR(t)
+  
+#define SWITCH_CONTEXTS(from, to) \
+  SAVE_TCB(from);                                \
+  RESTORE_TCB(to)
+
+#define PREPARE_THREAD(t, start_function)                             \
+  t->stack_ptr[0] = 0; \
+  t->stack_ptr[-1] = (uint8_t)((uint16_t)&start_function >> 8) & 0xFF; \
+  t->stack_ptr[-2] = (uint8_t)((uint16_t)&start_function) & 0xFF; \
+  t->stack_ptr -= 2; \
+  SAVE_STATUS(t) 
diff --git a/tos/lib/tosthreads/platforms/mulle/ActiveMessageC.nc b/tos/lib/tosthreads/platforms/mulle/ActiveMessageC.nc
new file mode 100644 (file)
index 0000000..21b7144
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2007, Vanderbilt University
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE VANDERBILT UNIVERSITY BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE VANDERBILT
+ * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE VANDERBILT UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE VANDERBILT UNIVERSITY HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Author: Miklos Maroti
+ */
+
+/*
+ * Make active message TOSThreads-compatible by exposing default interfaces
+ *
+ * Author: Chieh-Jan Mike Liang
+ */
+
+#include <RadioConfig.h>
+
+configuration ActiveMessageC
+{
+       provides
+       {
+               interface SplitControl;
+
+               interface AMSend[uint8_t id];
+               interface Receive[uint8_t id];
+    interface Receive as ReceiveDefault[am_id_t id];
+               interface Receive as Snoop[uint8_t id];
+    interface Receive as SnoopDefault[am_id_t id];
+               interface SendNotifier[am_id_t id];
+
+               interface Packet;
+               interface AMPacket;
+
+               interface PacketAcknowledgements;
+               interface LowPowerListening;
+#ifdef PACKET_LINK
+               interface PacketLink;
+#endif
+
+               interface PacketTimeStamp<TMicro, uint32_t> as PacketTimeStampMicro;
+               interface PacketTimeStamp<TMilli, uint32_t> as PacketTimeStampMilli;
+       }
+}
+
+implementation
+{
+       components RF230ActiveMessageC as MessageC,
+               RF230SplitControlP,
+               new SystemClockControlC();
+
+       RF230SplitControlP.SplitControlOrig -> MessageC;
+       RF230SplitControlP.SystemClockControl -> SystemClockControlC;
+
+       SplitControl = RF230SplitControlP.SplitControl;
+
+       AMSend = MessageC;
+       Receive = MessageC.Receive;
+  ReceiveDefault = MessageC.ReceiveDefault;
+  Snoop        = MessageC.Snoop;
+  SnoopDefault = MessageC.SnoopDefault;
+       SendNotifier = MessageC;
+
+       Packet = MessageC;
+       AMPacket = MessageC;
+
+       PacketAcknowledgements = MessageC;
+       LowPowerListening = MessageC;
+#ifdef PACKET_LINK
+       PacketLink = MessageC;
+#endif
+
+       PacketTimeStampMilli = MessageC;
+       PacketTimeStampMicro = MessageC;
+}
diff --git a/tos/platforms/mulle/fix/At45dbP.nc b/tos/platforms/mulle/fix/At45dbP.nc
new file mode 100644 (file)
index 0000000..91fb7b8
--- /dev/null
@@ -0,0 +1,469 @@
+// $Id$
+
+/*
+ * "Copyright (c) 2000-2003 The Regents of the University  of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ *
+ * Copyright (c) 2002-2005 Intel Corporation
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached INTEL-LICENSE     
+ * file. If you do not find these files, copies can be found by writing to
+ * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
+ * 94704.  Attention:  Intel License Inquiry.
+ */
+
+#include "crc.h"
+#include "At45db.h"
+#include "Timer.h"
+
+/**
+ * Private componenent for the Atmel's AT45DB HAL.
+ *
+ * @author David Gay
+ */
+
+module At45dbP @safe() {
+  provides {
+    interface Init;
+    interface At45db;
+  }
+  uses {
+    interface HplAt45db;
+    interface BusyWait<TMicro, uint16_t>;
+  }
+}
+implementation
+{
+#define CHECKARGS
+
+#if 0
+  uint8_t work[20];
+  uint8_t woffset;
+
+  void wdbg(uint8_t x) {
+    work[woffset++] = x;
+    if (woffset == sizeof work)
+      woffset = 0;
+  }
+#else
+#define wdbg(n)
+#endif
+
+  enum { // requests
+    IDLE,
+    R_READ,
+    R_READCRC,
+    R_WRITE,
+    R_ERASE,
+    R_COPY,
+    R_SYNC,
+    R_SYNCALL,
+    R_FLUSH,
+    R_FLUSHALL,
+    BROKEN // Write failed. Fail all subsequent requests.
+  };
+  uint8_t request;
+  at45pageoffset_t reqOffset, reqBytes;
+  uint8_t * COUNT_NOK(reqBytes) reqBuf;
+  at45page_t reqPage;
+
+  enum {
+    P_READ,
+    P_READCRC,
+    P_WRITE,
+    P_FLUSH,
+    P_FILL,
+    P_ERASE,
+    P_COMPARE,
+    P_COMPARE_CHECK
+  };
+  
+  struct {
+    at45page_t page;
+    bool busy : 1;
+    bool clean : 1;
+    bool erased : 1;
+    uint8_t unchecked : 2;
+  } buffer[2];
+  uint8_t selected; // buffer used by the current op
+  uint8_t checking;
+  bool flashBusy;
+
+  // Select a command for the current buffer
+#define OPN(n, name) ((n) ? name ## 1 : name ## 2)
+#define OP(name) OPN(selected, name)
+
+  command error_t Init.init() {
+    request = IDLE;
+    flashBusy = TRUE;
+      
+    // pretend we're on an invalid non-existent page
+    buffer[0].page = buffer[1].page = AT45_MAX_PAGES;
+    buffer[0].busy = buffer[1].busy = FALSE;
+    buffer[0].clean = buffer[1].clean = TRUE;
+    buffer[0].unchecked = buffer[1].unchecked = 0;
+    buffer[0].erased = buffer[1].erased = FALSE;
+
+    return SUCCESS;
+  }
+  
+  void flashIdle() {
+    flashBusy = buffer[0].busy = buffer[1].busy = FALSE;
+  }
+
+  void requestDone(error_t result, uint16_t computedCrc, uint8_t newState);
+  void handleRWRequest();
+
+  task void taskSuccess() {
+    requestDone(SUCCESS, 0, IDLE);
+  }
+  task void taskFail() {
+    requestDone(FAIL, 0, IDLE);
+  }
+
+  void checkBuffer(uint8_t buf) {
+    if (flashBusy)
+      {
+       call HplAt45db.waitIdle();
+       return;
+      }
+    call HplAt45db.compare(OPN(buf, AT45_C_COMPARE_BUFFER), buffer[buf].page);
+    checking = buf;
+  }
+
+  void flushBuffer() {
+    if (flashBusy)
+      {
+       call HplAt45db.waitIdle();
+       return;
+      }
+    call HplAt45db.flush(buffer[selected].erased ?
+                        OP(AT45_C_QFLUSH_BUFFER) :
+                        OP(AT45_C_FLUSH_BUFFER), 
+                        buffer[selected].page);
+  }
+
+  event void HplAt45db.waitIdleDone() {
+    flashIdle();
+    // Eager compare - this steals the current command
+#if 0
+    if ((buffer[0].unchecked || buffer[1].unchecked) &&
+       cmdPhase != P_COMPARE)
+      checkBuffer(buffer[0].unchecked ? 0 : 1);
+    else
+#endif
+      handleRWRequest();
+  }
+
+  event void HplAt45db.waitCompareDone(bool ok) {
+    flashIdle();
+
+    if (ok)
+      buffer[checking].unchecked = 0;
+    else if (buffer[checking].unchecked < 2)
+      buffer[checking].clean = FALSE;
+    else
+      {
+       requestDone(FAIL, 0, BROKEN);
+       return;
+      }
+    handleRWRequest();
+  }
+
+  event void HplAt45db.readDone() {
+    requestDone(SUCCESS, 0, IDLE);
+  }
+
+  event void HplAt45db.writeDone() {
+    buffer[selected].clean = FALSE;
+    buffer[selected].unchecked = 0;
+    requestDone(SUCCESS, 0, IDLE);
+  }
+
+  event void HplAt45db.crcDone(uint16_t crc) {
+    requestDone(SUCCESS, crc, IDLE);
+  }
+
+  event void HplAt45db.flushDone() {
+    flashBusy = TRUE;
+    buffer[selected].clean = buffer[selected].busy = TRUE;
+    buffer[selected].unchecked++;
+    buffer[selected].erased = FALSE;
+    handleRWRequest();
+  }
+
+  event void HplAt45db.compareDone() {
+    flashBusy = TRUE;
+    buffer[checking].busy = TRUE;
+    // The 10us wait makes old mica motes (Atmega 103) happy, for
+    // some mysterious reason (w/o this wait, the first compare
+    // always fails, even though the compare after the rewrite
+    // succeeds...)
+    call BusyWait.wait(10);
+    call HplAt45db.waitCompare();
+  }
+
+  event void HplAt45db.fillDone() {
+    flashBusy = TRUE;
+    buffer[selected].page = reqPage;
+    buffer[selected].clean = buffer[selected].busy = TRUE;
+    buffer[selected].erased = FALSE;
+    handleRWRequest();
+  }
+
+  event void HplAt45db.eraseDone() {
+    flashBusy = TRUE;
+    // The buffer contains garbage, but we don't care about the state
+    // of bits on this page anyway (if we do, we'll perform a 
+    // subsequent write)
+    buffer[selected].page = AT45_MAX_PAGES;
+    buffer[selected].clean = TRUE;
+    buffer[selected].erased = TRUE;
+    requestDone(SUCCESS, 0, IDLE);
+  }
+
+  void syncOrFlushAll(uint8_t newReq);
+
+  void handleRWRequest() {
+    if (reqPage == buffer[selected].page)
+      switch (request)
+       {
+       case R_ERASE:
+         switch (reqOffset)
+           {
+           case AT45_ERASE:
+             if (flashBusy)
+               call HplAt45db.waitIdle();
+             else
+               call HplAt45db.erase(AT45_C_ERASE_PAGE, reqPage);
+             break;
+           case AT45_PREVIOUSLY_ERASED:
+             // We believe the user...
+             buffer[selected].erased = TRUE;
+             /* Fallthrough */
+           case AT45_DONT_ERASE:
+             // The buffer contains garbage, but we don't care about the state
+             // of bits on this page anyway (if we do, we'll perform a 
+             // subsequent write)
+             buffer[selected].clean = TRUE;
+             requestDone(SUCCESS, 0, IDLE);
+             break;
+           }
+         break;
+
+       case R_COPY:
+         if (!buffer[selected].clean) // flush any modifications
+           flushBuffer();
+         else
+           {
+             // Just redesignate as destination page, and mark it dirty.
+             // It will eventually be flushed, completing the copy.
+             buffer[selected].page = reqOffset;
+             buffer[selected].clean = FALSE;
+             post taskSuccess();
+           }
+         break;
+
+       case R_SYNC: case R_SYNCALL:
+         if (buffer[selected].clean && buffer[selected].unchecked)
+           {
+             checkBuffer(selected);
+             return;
+           }
+         /* fall through */
+       case R_FLUSH: case R_FLUSHALL:
+         if (!buffer[selected].clean)
+           flushBuffer();
+         else if (request == R_FLUSH || request == R_SYNC)
+           post taskSuccess();
+         else
+           {
+             // Check for more dirty pages
+             uint8_t oreq = request;
+
+             request = IDLE;
+             syncOrFlushAll(oreq);
+           }
+         break;
+
+       case R_READ:
+         if (buffer[selected].busy)
+           call HplAt45db.waitIdle();
+         else
+           call HplAt45db.readBuffer(OP(AT45_C_READ_BUFFER), reqOffset,
+                                     reqBuf, reqBytes);
+         break;
+
+       case R_READCRC:
+         if (buffer[selected].busy)
+           call HplAt45db.waitIdle();
+         else
+           /* Hack: baseCrc was stored in reqBuf */
+           call HplAt45db.crc(OP(AT45_C_READ_BUFFER), 0, reqOffset, reqBytes,
+                              (uint16_t)reqBuf);
+         break;
+
+       case R_WRITE:
+         if (buffer[selected].busy)
+           call HplAt45db.waitIdle();
+         else
+           call HplAt45db.write(OP(AT45_C_WRITE_BUFFER), 0, reqOffset,
+                                reqBuf, reqBytes);
+         break;
+       }
+    else if (!buffer[selected].clean)
+      flushBuffer();
+    else if (buffer[selected].unchecked)
+      checkBuffer(selected);
+    else
+      {
+       // just get the new page (except for erase)
+       if (request == R_ERASE)
+         {
+           buffer[selected].page = reqPage;
+           handleRWRequest();
+         }
+       else if (flashBusy)
+         call HplAt45db.waitIdle();
+       else
+         call HplAt45db.fill(OP(AT45_C_FILL_BUFFER), reqPage);
+      }
+  }
+
+  void requestDone(error_t result, uint16_t computedCrc, uint8_t newState) {
+    uint8_t orequest = request;
+
+    request = newState;
+    switch (orequest)
+      {
+      case R_READ: signal At45db.readDone(result); break;
+      case R_READCRC: signal At45db.computeCrcDone(result, computedCrc); break;
+      case R_WRITE: signal At45db.writeDone(result); break;
+      case R_SYNC: case R_SYNCALL: signal At45db.syncDone(result); break;
+      case R_FLUSH: case R_FLUSHALL: signal At45db.flushDone(result); break;
+      case R_ERASE: signal At45db.eraseDone(result); break;
+      case R_COPY: signal At45db.copyPageDone(result); break;
+      }
+  }
+
+  void newRequest(uint8_t req, at45page_t page, at45pageoffset_t offset,
+                 void * COUNT_NOK(n) reqdata, at45pageoffset_t n) {
+    request = req;
+
+    reqBuf = NULL;
+    reqBytes = n;
+    reqBuf = reqdata;
+    reqPage = page;
+    reqOffset = offset;
+
+    if (page == buffer[0].page)
+      selected = 0;
+    else if (page == buffer[1].page)
+      selected = 1;
+    else
+      selected = !selected; // LRU with 2 buffers...
+
+#ifdef CHECKARGS
+    if (page >= AT45_MAX_PAGES || offset >= AT45_PAGE_SIZE ||
+       n > AT45_PAGE_SIZE || offset + n > AT45_PAGE_SIZE)
+      post taskFail();
+    else
+#endif
+      handleRWRequest();
+  }
+
+  command void At45db.read(at45page_t page, at45pageoffset_t offset,
+                                  void *reqdata, at45pageoffset_t n) {
+    newRequest(R_READ, page, offset, reqdata, n);
+  }
+
+  command void At45db.computeCrc(at45page_t page,
+                                       at45pageoffset_t offset,
+                                       at45pageoffset_t n,
+                                       uint16_t baseCrc) {
+    /* This is a hack (store crc in reqBuf), but it saves 2 bytes of RAM */
+    newRequest(R_READCRC, page, offset, TCAST(uint8_t * COUNT(n), baseCrc), n);
+  }
+
+  command void At45db.write(at45page_t page, at45pageoffset_t offset,
+                                   void *reqdata, at45pageoffset_t n) {
+    newRequest(R_WRITE, page, offset, reqdata, n);
+  }
+
+
+  command void At45db.erase(at45page_t page, uint8_t eraseKind) {
+    newRequest(R_ERASE, page, eraseKind, NULL, 0);
+  }
+
+  command void At45db.copyPage(at45page_t from, at45page_t to) {
+    /* Assumes at45pageoffset_t can hold an at45page_t. A little icky */
+    newRequest(R_COPY, from, to, NULL, 0);
+  }
+
+  void syncOrFlush(at45page_t page, uint8_t newReq) {
+    request = newReq;
+
+    if (buffer[0].page == page)
+      selected = 0;
+    else if (buffer[1].page == page)
+      selected = 1;
+    else
+      {
+       post taskSuccess();
+       return;
+      }
+
+    buffer[selected].unchecked = 0;
+    handleRWRequest();
+  }
+
+  command void At45db.sync(at45page_t page) {
+    syncOrFlush(page, R_SYNC);
+  }
+
+  command void At45db.flush(at45page_t page) {
+    syncOrFlush(page, R_FLUSH);
+  }
+
+  void syncOrFlushAll(uint8_t newReq) {
+    request = newReq;
+
+    if (!buffer[0].clean)
+      selected = 0;
+    else if (!buffer[1].clean)
+      selected = 1;
+    else
+      {
+       post taskSuccess();
+       return;
+      }
+
+    buffer[selected].unchecked = 0;
+    handleRWRequest();
+  }
+
+  command void At45db.syncAll() {
+    syncOrFlushAll(R_SYNCALL);
+  }
+
+  command void At45db.flushAll() {
+    syncOrFlushAll(R_FLUSHALL);
+  }
+}
diff --git a/tos/platforms/mulle/fix/LogStorageP.nc b/tos/platforms/mulle/fix/LogStorageP.nc
new file mode 100644 (file)
index 0000000..5606efa
--- /dev/null
@@ -0,0 +1,993 @@
+/*
+ * "Copyright (c) 2000-2004 The Regents of the University  of California.  
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose, without fee, and without written agreement is
+ * hereby granted, provided that the above copyright notice, the following
+ * two paragraphs and the author appear in all copies of this software.
+ * 
+ * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
+ * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
+ * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."
+ *
+ * Copyright (c) 2002-2006 Intel Corporation
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached INTEL-LICENSE     
+ * file. If you do not find these files, copies can be found by writing to
+ * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, 
+ * 94704.  Attention:  Intel License Inquiry.
+ */
+
+#include <Storage.h>
+#include <crc.h>
+
+/**
+ * Private component of the AT45DB implementation of the log storage
+ * abstraction.
+ *
+ * @author: David Gay <dgay@acm.org>
+ * @author: Jonathan Hui <jwhui@cs.berkeley.edu>
+ */
+
+module LogStorageP @safe() {
+  provides {
+    interface LogRead[uint8_t logId];
+    interface LogWrite[uint8_t logId];
+  }
+  uses {
+    interface At45db;
+    interface At45dbVolume[uint8_t logId];
+    interface Resource[uint8_t logId];
+  }
+}
+implementation
+{
+  /* Some design notes.
+
+  - The logId's in the LogRead and LogWrites are shifted left by 1 bit.
+    The low-order bit is 1 for circular logs, 0 for linear ones
+    (see newRequest and endRequest, and the LogStorageC configuration)
+
+  - Data is written sequentially to the pages of a log volume. Each page
+    ends with a footer (nx_struct pageinfo) recording metadata on the
+    current page:
+    o a cookie
+    o the "position" of the current page in the log (see below)
+    o the offset of the last record on this page (i.e., the offset
+      at which the last append ended) - only valid if flags & F_LASTVALID
+    o flags: 
+      x F_SYNC page was synchronised - data after lastRecordOffset
+        is not log data; implies F_LASTVALID
+      x F_CIRCLED this page is not from the first run through the log's
+        pages (never set in linear logs)
+      x F_LASTVALID not set if no record ended on this page
+    o a CRC
+
+  - "Positions" are stored in the metadata, used as cookies by
+    currentOffset and seek, and stored in the wpos and rpos fields of the
+    volume state structure.  They represent the number of bytes that
+    writing has advanced in the log since the log was erased, with
+    PAGE_SIZE added. Note that this is basically the number of bytes
+    written, except that when a page is synchronised unused bytes in the
+    page count towards increasing the position.
+
+    As a result, on page p, the following equation holds:
+      (metadata(p).pos - PAGE_SIZE) % volume-size == p * PAGE_SIZE
+    (this also means that the "position" metadata field could be replaced
+    by a count of the number of times writing has cycled through the log,
+    reducing the metadata size)
+
+    The PAGE_SIZE offset on positions is caused by Invariant 2 below: to
+    ensure that Invariant 2 is respected, at flash erase time, we write a
+    valid page with position 0 to the last block of the flash. As a result,
+    the first writes to the flash, in page 0, are at "position" PAGE_SIZE.
+
+  - This code is designed to deal with "one-at-a-time" failures (i.e.,
+    the system will not modify any blocks after a previous failed
+    write). This should allow recovery from:
+    o arbitrary reboots
+    o write failure (the underlying PageEEPROM shuts down after any
+      write fails; all pages are flushed before moving on to the next
+      page)
+    It will not recover from arbitrary data corruption
+
+  - When sync is called, the current write page is written to flash with an
+    F_SYNC flag and writing continues on the next page (wasting on average
+    half a flasg page)
+
+  - We maintain the following invariants on log volumes, even in the face
+    of the "one-at-a-time" failures described above:
+    1) at least one of the first and last blocks are valid
+    2) the last block, if valid, has the F_SYNC flag
+
+  - Locating the log boundary page (the page with the greatest position):
+
+    Invariant 1, the one-at-a-time failure model and the metadata position
+    definition guarantees that the physical flash pages have the following
+    properties:
+      an initial set of V1 valid pages,
+      followed by a set of I invalid pages,
+      followed by a set of V2 valid pages
+    with V1+i+V2=total-number-of-pages, and V1, V2, I >= 0
+    Additionally, the position of all pages in V1 is greater than in V2,
+    and consecutive pages in V1 (respectively V2) have greater positions
+    than their predecessors.
+
+    From this, it's possible to locate the log boundary page (the page with
+    the greatest position) using the following algorithm:
+    o let basepos=metadata(lastpage).pos, or 0 if the last page is invalid
+    o locate (using a binary search) the page p with the largest position
+      greater than basepos
+      invalid pages can be assumed to have positions less than basepos
+      if there is no such page p, let p = lastpage
+
+    Once the log boundary page is known, we resume writing at the last
+    page before p with a record boundary (Invariant 2, combined with
+    limiting individual records to volumesize - PAGE_SIZE ensures there
+    will be such a page).
+
+  - The read pointer has a special "invalid" state which represents the
+    current beginning of the log. In that state, LogRead.currentOffset()
+    returns SEEK_BEGINNING rather than a regular position.
+
+    The read pointer is invalidated:
+    o at boot time
+    o after the volume is erased
+    o after the write position "catches up" with the read position
+    o after a failed seek
+
+    Reads from an invalid pointer:
+    o start reading from the beginning of the flash if we are on the
+      first run through the log volume
+    o start reading at the first valid page after the write page with
+      an F_LASTVALID flag; the read offset is set to the lastRecordOffset
+      value
+      if this page has the SYNC flag, we start at the beginning of the
+      next page
+  */
+             
+
+  enum {
+    F_SYNC = 1,
+    F_CIRCLED = 2,
+    F_LASTVALID = 4
+  };
+
+  nx_struct pageinfo {
+    nx_uint16_t magic;
+    nx_uint32_t pos;
+    nx_uint16_t lastRecordOffset;
+    nx_uint8_t flags;
+    nx_uint16_t crc;
+  };
+
+  enum {
+    N = uniqueCount(UQ_LOG_STORAGE),
+    NO_CLIENT = 0xff,
+    PAGE_SIZE = AT45_PAGE_SIZE - sizeof(nx_struct pageinfo),
+    PERSISTENT_MAGIC = 0x4256,
+  };
+
+  enum {
+    R_IDLE,
+    R_ERASE,
+    R_APPEND,
+    R_SYNC,
+    R_READ,
+    R_SEEK
+  };
+
+  enum {
+    META_IDLE,
+    META_LOCATEFIRST,
+    META_LOCATE,
+    META_LOCATELAST,
+    META_SEEK,
+    META_READ,
+    META_WRITE
+  };
+
+  uint8_t client = NO_CLIENT;
+  uint8_t metaState;
+  bool recordsLost;
+  at45page_t firstPage, lastPage;
+  storage_len_t pos;
+  nx_struct pageinfo metadata;
+
+  struct {
+    /* The latest request made for this client, and it's arguments */
+    uint8_t request; 
+    uint8_t *COUNT_NOK(len) buf;
+    storage_len_t len;
+
+    /* Log r/w positions */
+    bool positionKnown : 1;
+    bool circular : 1;
+    bool circled : 1;
+    bool rvalid : 1;
+    uint32_t wpos;             /* Bytes since start of logging */
+    at45page_t wpage;          /* Current write page */
+    at45pageoffset_t woffset;  /* Offset on current write page */
+    uint32_t rpos;             /* Bytes since start of logging */
+    at45page_t rpage;          /* Current read page */
+    at45pageoffset_t roffset;  /* Offset on current read page */
+    at45pageoffset_t rend;     /* Last valid offset on current read page */
+  } s[N];
+
+  at45page_t firstVolumePage() {
+    return call At45dbVolume.remap[client](0);
+  }
+
+  at45page_t npages() {
+    return call At45dbVolume.volumeSize[client]();
+  }
+
+  at45page_t lastVolumePage() {
+    return call At45dbVolume.remap[client](npages());
+  }
+
+  void setWritePage(at45page_t page) {
+    if (s[client].circular && page == lastVolumePage())
+      {
+       s[client].circled = TRUE;
+       page = firstVolumePage();
+      }
+    s[client].wpage = page;
+    s[client].woffset = 0;
+  }
+
+  void invalidateReadPointer() {
+    s[client].rvalid = FALSE;
+  }
+
+  void crcPage(at45page_t page) {
+    call At45db.computeCrc(page, 0,
+                          PAGE_SIZE + offsetof(nx_struct pageinfo, crc), 0);
+  }
+
+  void readMetadata(at45page_t page) {
+    call At45db.read(page, PAGE_SIZE, &metadata, sizeof metadata);
+  }
+
+  void writeMetadata(at45page_t page) {
+    call At45db.write(page, PAGE_SIZE, &metadata, sizeof metadata);
+  }
+
+  void wmetadataStart();
+
+  void sync() {
+    metadata.flags = F_SYNC | F_LASTVALID;
+    metadata.lastRecordOffset = s[client].woffset;
+    /* rend is now no longer the end of the page */
+    if (s[client].rpage == s[client].wpage)
+      s[client].rend = s[client].woffset;
+    wmetadataStart();
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Queue and initiate user requests                                  */
+  /* ------------------------------------------------------------------ */
+
+  void eraseStart();
+  void appendStart();
+  void syncStart();
+  void readStart();
+  void locateStart();
+  void rmetadataStart();
+  void seekStart();
+
+  void startRequest() {
+    if (!s[client].positionKnown && s[client].request != R_ERASE)
+      {
+       locateStart();
+       return;
+      }
+
+    metaState = META_IDLE;
+    switch (s[client].request)
+      {
+      case R_ERASE: eraseStart(); break;
+      case R_APPEND: appendStart(); break;
+      case R_SYNC: syncStart(); break;
+      case R_READ: readStart(); break;
+      case R_SEEK: seekStart(); break;
+      }
+  }
+
+  void endRequest(error_t ok) {
+    uint8_t c = client;
+    uint8_t request = s[c].request;
+    storage_len_t actualLen = pos;
+    void *ptr = s[c].buf;
+    
+    client = NO_CLIENT;
+    s[c].request = R_IDLE;
+    call Resource.release[c]();
+
+    c = c << 1 | s[c].circular;
+    switch (request)
+      {
+      case R_ERASE: signal LogWrite.eraseDone[c](ok); break;
+      case R_APPEND: signal LogWrite.appendDone[c](ptr, actualLen, recordsLost, ok); break;
+      case R_SYNC: signal LogWrite.syncDone[c](ok); break;
+      case R_READ: signal LogRead.readDone[c](ptr, actualLen, ok); break;
+      case R_SEEK: signal LogRead.seekDone[c](ok); break;
+      }
+  }
+
+  /* Enqueue request and request the underlying flash */
+  error_t newRequest(uint8_t newRequest, uint8_t id,
+                    uint8_t *COUNT_NOK(length) buf, storage_len_t length) {
+    s[id >> 1].circular = id & 1;
+    id >>= 1;
+
+    if (s[id].request != R_IDLE)
+      return EBUSY;
+
+    s[id].request = newRequest;
+    s[id].buf = NULL;
+    s[id].len = length;
+    s[id].buf = buf;
+    call Resource.request[id]();
+
+    return SUCCESS;
+  }
+
+  event void Resource.granted[uint8_t id]() {
+    client = id;
+    pos = 0;
+    startRequest();
+  }
+
+  command error_t LogWrite.append[uint8_t id](void* buf, storage_len_t length) {
+    if (length > call LogRead.getSize[id]() - PAGE_SIZE)
+      /* Writes greater than the volume size are invalid.
+        Writes equal to the volume size could break the log volume
+        invariant (see next comment).
+        Writes that span the whole volume could lead to problems
+        at boot time (no valid block with a record boundary).
+        Refuse them all. */
+      return EINVAL;
+    else
+      return newRequest(R_APPEND, id, buf, length);
+  }
+
+  command storage_cookie_t LogWrite.currentOffset[uint8_t id]() {
+    return s[id >> 1].wpos;
+  }
+
+  command error_t LogWrite.erase[uint8_t id]() {
+    return newRequest(R_ERASE, id, NULL, 0);
+  }
+
+  command error_t LogWrite.sync[uint8_t id]() {
+    return newRequest(R_SYNC, id, NULL, 0);
+  }
+
+  command error_t LogRead.read[uint8_t id](void* buf, storage_len_t length) {
+    return newRequest(R_READ, id, buf, length);
+  }
+
+  command storage_cookie_t LogRead.currentOffset[uint8_t id]() {
+    id >>= 1;
+    return s[id].rvalid ? s[id].rpos : SEEK_BEGINNING;
+  }
+
+  command error_t LogRead.seek[uint8_t id](storage_cookie_t offset) {
+    return newRequest(R_SEEK, id, TCAST(void *COUNT(offset), ((uint16_t)(offset >> 16))), offset);
+  }
+
+  command storage_len_t LogRead.getSize[uint8_t id]() {
+    return call At45dbVolume.volumeSize[id >> 1]() * (storage_len_t)PAGE_SIZE;
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Erase                                                             */
+  /* ------------------------------------------------------------------ */
+
+  void eraseMetadataDone() {
+    /* Set write pointer to the beginning of the flash */
+    s[client].wpos = PAGE_SIZE; // last page has offset 0 and is before us
+    s[client].circled = FALSE;
+    setWritePage(firstVolumePage()); 
+
+    invalidateReadPointer();
+
+    s[client].positionKnown = TRUE;
+    endRequest(SUCCESS);
+  }
+
+  void eraseEraseDone() {
+    if (firstPage == lastPage - 1)
+      {
+       /* We create a valid, synced last page (see invariants) */
+       metadata.flags = F_SYNC | F_LASTVALID;
+       metadata.lastRecordOffset = 0;
+       setWritePage(firstPage);
+       s[client].circled = FALSE;
+       s[client].wpos = 0;
+       wmetadataStart();
+      }
+    else
+      call At45db.erase(firstPage++, AT45_ERASE);
+  }
+
+  void eraseStart() {
+    s[client].positionKnown = FALSE; // in case erase fails
+    firstPage = firstVolumePage();
+    lastPage = lastVolumePage();
+    eraseEraseDone();
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Locate log boundaries                                             */
+  /* ------------------------------------------------------------------ */
+
+  void locateLastRecord();
+
+  void locateLastCrcDone(uint16_t crc) {
+    if (crc != metadata.crc)
+      {
+       locateLastRecord();
+       return;
+      }
+
+    /* We've found the last valid page with a record-end. Set up
+       the read and write positions. */
+    invalidateReadPointer();
+
+    if (metadata.flags & F_SYNC) /* must start on next page */
+      {
+       /* We need to special case the empty log, as we don't want
+          to wrap around in the case of a full, non-circular log
+          with a sync on its last page. */
+       if (firstPage == lastPage && !metadata.pos)
+         setWritePage(firstVolumePage());
+       else
+         setWritePage(firstPage + 1);
+       s[client].wpos = metadata.pos + PAGE_SIZE;
+      }
+    else
+      {
+       s[client].wpage = firstPage;
+       s[client].woffset = metadata.lastRecordOffset;
+       s[client].wpos = metadata.pos + metadata.lastRecordOffset;
+      }
+
+    s[client].circled = (metadata.flags & F_CIRCLED) != 0;
+    if (s[client].circled && !s[client].circular) // oops
+      {
+       endRequest(FAIL);
+       return;
+      }
+
+    /* And we can now proceed to the real request */
+    s[client].positionKnown = TRUE;
+    startRequest();
+  }
+
+  void locateLastReadDone() {
+    if (metadata.magic == PERSISTENT_MAGIC && metadata.flags & F_LASTVALID)
+      crcPage(firstPage);
+    else
+      locateLastRecord();
+  }
+
+  void locateLastRecord() {
+    if (firstPage == lastPage)
+      {
+       /* We walked all the way back to the last page, and it's not 
+          valid. The log-volume invariant is not holding. Fail out. */
+       endRequest(FAIL);
+       return;
+      }
+
+    if (firstPage == firstVolumePage())
+      firstPage = lastPage;
+    else
+      firstPage--;
+
+    readMetadata(firstPage);
+  }
+
+  void located() {
+    metaState = META_LOCATELAST;
+    /* firstPage is one after last valid page, but the last page with
+       a record end may be some pages earlier. Search for it. */
+    lastPage = lastVolumePage() - 1;
+    locateLastRecord();
+  }
+
+  at45page_t locateCurrentPage() {
+    return firstPage + ((lastPage - firstPage) >> 1);
+  }
+
+  void locateBinarySearch() {
+    if (lastPage <= firstPage)
+      located();
+    else
+      readMetadata(locateCurrentPage());
+  }
+
+  void locateGreaterThan() {
+    firstPage = locateCurrentPage() + 1;
+    locateBinarySearch();
+  }
+
+  void locateLessThan() {
+    lastPage = locateCurrentPage();
+    locateBinarySearch();
+  }
+
+  void locateCrcDone(uint16_t crc) {
+    if (crc == metadata.crc)
+      {
+       s[client].wpos = metadata.pos;
+       locateGreaterThan();
+      }
+    else
+      locateLessThan();
+  }
+
+  void locateReadDone() {
+    if (metadata.magic == PERSISTENT_MAGIC && s[client].wpos < metadata.pos)
+      crcPage(locateCurrentPage());
+    else
+      locateLessThan();
+  }
+
+  void locateFirstCrcDone(uint16_t crc) {
+    if (metadata.magic == PERSISTENT_MAGIC && crc == metadata.crc)
+      s[client].wpos = metadata.pos;
+    else
+      s[client].wpos = 0;
+
+    metaState = META_LOCATE;
+    locateBinarySearch();
+  }
+
+  void locateFirstReadDone() {
+    crcPage(lastPage);
+  }
+
+  /* Locate log beginning and ending. See description at top of file. */
+  void locateStart() {
+    metaState = META_LOCATEFIRST;
+    firstPage = firstVolumePage();
+    lastPage = lastVolumePage() - 1;
+    readMetadata(lastPage);
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Append                                                            */
+  /* ------------------------------------------------------------------ */
+
+  void appendContinue() {
+    uint8_t *buf = s[client].buf + pos;
+    at45pageoffset_t offset = s[client].woffset, count;
+    storage_len_t len = s[client].len - pos;
+    
+    if (len == 0)
+      {
+       endRequest(SUCCESS);
+       return;
+      }
+
+    if (s[client].wpage == lastVolumePage())
+      {
+       /* We reached the end of a linear log */
+       endRequest(ESIZE);
+       return;
+      }
+
+    if (offset + len <= PAGE_SIZE)
+      count = len;
+    else
+      count = PAGE_SIZE - offset;
+
+    s[client].wpos += count;
+    s[client].woffset += count;
+    pos += count;
+
+    /* We normally lose data at the point we make the first write to a
+       page in a log that has circled. */
+    if (offset == 0 && s[client].circled)
+      recordsLost = TRUE;
+
+    call At45db.write(s[client].wpage, offset, buf, count);
+  }
+  
+  void appendWriteDone() {
+    if (s[client].woffset == PAGE_SIZE) /* Time to write metadata */
+      wmetadataStart();
+    else
+      endRequest(SUCCESS);
+  }
+
+  void appendMetadataDone() { // metadata of previous page flushed
+    /* Setup metadata in case we overflow this page too */
+    metadata.flags = 0;
+    appendContinue();
+  }
+
+  void appendSyncDone() {
+    s[client].wpos = metadata.pos + PAGE_SIZE;
+    appendStart();
+  }
+
+  void appendStart() {
+    storage_len_t len = s[client].len - pos;
+    storage_len_t vlen = (storage_len_t)npages() * PAGE_SIZE;
+
+    recordsLost = FALSE;
+
+    /* If request would span the end of the flash, sync, to maintain the
+       invariant that the last flash page is synced and that either
+       the first or last pages are valid.
+
+       Note that >= in the if below means we won't write a record that
+       would end on the last byte of the last page, as this would mean that
+       we would not sync the last page, breaking the log volume
+       invariant */
+    if ((s[client].wpos - PAGE_SIZE) % vlen >= vlen - len)
+      sync();
+    else
+      {
+       /* Set lastRecordOffset in case we need to write metadata (see
+          wmetadataStart) */
+       metadata.lastRecordOffset = s[client].woffset;
+       metadata.flags = F_LASTVALID;
+       appendContinue();
+      }
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Sync                                                              */
+  /* ------------------------------------------------------------------ */
+
+  void syncStart() {
+    if (s[client].woffset == 0) /* we can't lose any writes */
+      endRequest(SUCCESS);
+    else
+      sync();
+  }
+
+  void syncMetadataDone() {
+    /* Write position reflect the absolute position in the flash, not
+       user-bytes written. So update wpos to reflect sync effects. */
+    s[client].wpos = metadata.pos + PAGE_SIZE;
+    endRequest(SUCCESS);
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Write block metadata                                              */
+  /* ------------------------------------------------------------------ */
+
+  void wmetadataStart() {
+    /* The caller ensures that metadata.flags (except F_CIRCLED) and
+       metadata.lastRecordOffset are set correctly. */
+    metaState = META_WRITE;
+    firstPage = s[client].wpage; // remember page to commit
+    metadata.pos = s[client].wpos - s[client].woffset;
+    metadata.magic = PERSISTENT_MAGIC;
+    if (s[client].circled)
+      metadata.flags |= F_CIRCLED;
+
+    call At45db.computeCrc(firstPage, 0, PAGE_SIZE, 0);
+
+    /* We move to the next page now. If writing the metadata fails, we'll
+       simply leave the invalid page in place. Trying to recover seems
+       complicated, and of little benefit (note that in practice, At45dbC
+       shuts down after a failed write, so nothing is really going to
+       happen after that anyway). */
+    setWritePage(s[client].wpage + 1);
+
+    /* Invalidate read pointer if we reach it's page */
+    if (s[client].wpage == s[client].rpage)
+      invalidateReadPointer();
+  }
+
+  void wmetadataCrcDone(uint16_t crc) {
+    uint8_t i, *md;
+
+    // Include metadata in crc
+    md = (uint8_t *)&metadata;
+    for (i = 0; i < offsetof(nx_struct pageinfo, crc); i++)
+      crc = crcByte(crc, md[i]);
+    metadata.crc = crc;
+
+    // And save it
+    writeMetadata(firstPage);
+  }
+
+  void wmetadataWriteDone() {
+    metaState = META_IDLE;
+    if (metadata.flags & F_SYNC)
+      call At45db.sync(firstPage);
+    else
+      call At45db.flush(firstPage);
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Read                                                              */
+  /* ------------------------------------------------------------------ */
+
+  void readContinue() {
+    uint8_t *buf = s[client].buf + pos;
+    at45pageoffset_t offset = s[client].roffset, count;
+    at45pageoffset_t end = s[client].rend;
+    storage_len_t len = s[client].len - pos;
+    
+    if (len == 0)
+      {
+       endRequest(SUCCESS);
+       return;
+      }
+
+    if (!s[client].rvalid)
+      {
+       if (s[client].circled)
+         /* Find a valid page after wpage, skipping invalid pages */
+         s[client].rpage = s[client].wpage;
+       else
+         {
+           /* resume reading at the beginning of the first page */
+           s[client].rvalid = TRUE;
+           s[client].rpage = lastVolumePage() - 1;
+         }
+
+       rmetadataStart();
+       return;
+      }
+
+    if (s[client].rpage == s[client].wpage)
+      end = s[client].woffset;
+
+    if (offset == end)
+      {
+       if ((s[client].rpage + 1 == lastVolumePage() && !s[client].circular) ||
+           s[client].rpage == s[client].wpage)
+         endRequest(SUCCESS); // end of log
+       else
+         rmetadataStart();
+       return;
+      }
+
+    if (offset + len <= end)
+      count = len;
+    else
+      count = end - offset;
+
+    pos += count;
+    s[client].rpos += count;
+    s[client].roffset = offset + count;
+
+    call At45db.read(s[client].rpage, offset, buf, count);
+  }
+
+  void readStart() {
+    readContinue();
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Read block metadata                                               */
+  /* ------------------------------------------------------------------ */
+
+  void continueReadAt(at45pageoffset_t roffset) {
+    /* Resume reading at firstPage whose metadata is currently available
+       in the metadata variable */
+    metaState = META_IDLE;
+    s[client].rpos = metadata.pos + roffset;
+    s[client].rpage = firstPage;
+    s[client].roffset = roffset;
+    s[client].rend =
+      metadata.flags & F_SYNC ? metadata.lastRecordOffset : PAGE_SIZE;
+    s[client].rvalid = TRUE;
+    readContinue();
+  }
+
+  void rmetadataContinue() {
+    if (++firstPage == lastVolumePage())
+      firstPage = firstVolumePage();
+    if (firstPage == s[client].wpage)
+      if (!s[client].rvalid)
+       /* We cannot find a record boundary to start at (we've just
+          walked through the whole log...). Give up. */
+       endRequest(SUCCESS);
+      else
+       {
+         /* The current write page has no metadata yet, so we fake it */
+         metadata.flags = 0;
+         metadata.pos = s[client].wpos - s[client].woffset;
+         continueReadAt(0);
+       }
+    else
+      readMetadata(firstPage);
+  }
+
+  void rmetadataReadDone() {
+    if (metadata.magic == PERSISTENT_MAGIC)
+      crcPage(firstPage);
+    else
+      endRequest(SUCCESS);
+  }
+
+  void rmetadataCrcDone(uint16_t crc) {
+    if (!s[client].rvalid)
+      if (crc == metadata.crc && metadata.flags & F_LASTVALID)
+       continueReadAt(metadata.lastRecordOffset);
+      else
+       rmetadataContinue();
+    else 
+      if (crc == metadata.crc)
+       continueReadAt(0);
+      else
+       endRequest(SUCCESS);
+  }
+
+  void rmetadataStart() {
+    metaState = META_READ;
+    firstPage = s[client].rpage;
+    rmetadataContinue();
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Seek.                                                             */
+  /* ------------------------------------------------------------------ */
+
+  void seekCrcDone(uint16_t crc) {
+    if (metadata.magic == PERSISTENT_MAGIC && crc == metadata.crc &&
+       metadata.pos == s[client].rpos - s[client].roffset)
+      {
+       s[client].rvalid = TRUE;
+       if (metadata.flags & F_SYNC)
+         s[client].rend = metadata.lastRecordOffset;
+      }
+    endRequest(SUCCESS);
+  }
+
+  void seekReadDone() {
+    crcPage(s[client].rpage);
+  }
+
+  /* Move to position specified by cookie. */
+  void seekStart() {
+    uint32_t offset = (uint32_t)(uint16_t)s[client].buf << 16 | s[client].len;
+
+    invalidateReadPointer(); // default to beginning of log
+
+    /* The write positions are offset by PAGE_SIZE (see emptyLog) */
+
+    if (offset == SEEK_BEGINNING)
+      offset = PAGE_SIZE;
+
+    if (offset > s[client].wpos || offset < PAGE_SIZE)
+      {
+       endRequest(EINVAL);
+       return;
+      }
+
+    /* Cookies are just flash positions which continue incrementing as
+       you circle around and around. So we can just check the requested
+       page's metadata.pos field matches the cookie's value */
+    s[client].rpos = offset;
+    s[client].roffset = (offset - PAGE_SIZE) % PAGE_SIZE;
+    s[client].rpage = firstVolumePage() + ((offset - PAGE_SIZE) / PAGE_SIZE) % npages();
+    s[client].rend = PAGE_SIZE; // default to no sync flag
+
+    // The last page's metadata isn't written to flash yet. Special case it.
+    if (s[client].rpage == s[client].wpage)
+      {
+       /* If we're seeking within the current write page, just go there.
+          Otherwise, we're asking for an old version of the current page
+          so just keep the invalidated read pointer, i.e., read from
+          the beginning. */
+       if (offset >= s[client].wpos - s[client].woffset)
+         s[client].rvalid = TRUE;
+       endRequest(SUCCESS);
+      }
+    else
+      {
+       metaState = META_SEEK;
+       readMetadata(s[client].rpage);
+      }
+  }
+
+  /* ------------------------------------------------------------------ */
+  /* Dispatch HAL operations to current user op                                */
+  /* ------------------------------------------------------------------ */
+
+  event void At45db.eraseDone(error_t error) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else
+       eraseEraseDone();
+  }
+
+  event void At45db.writeDone(error_t error) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else
+       switch (metaState)
+         {
+         case META_WRITE: wmetadataWriteDone(); break;
+         case META_IDLE: appendWriteDone(); break;
+         }
+  }
+
+  event void At45db.syncDone(error_t error) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else switch (s[client].request)
+       {
+       case R_ERASE: eraseMetadataDone(); break;
+       case R_APPEND: appendSyncDone(); break;
+       case R_SYNC: syncMetadataDone(); break;
+       }
+  }
+
+  event void At45db.flushDone(error_t error) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else
+       appendMetadataDone();
+  }
+
+  event void At45db.readDone(error_t error) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else
+       switch (metaState)
+         {
+         case META_LOCATEFIRST: locateFirstReadDone(); break;
+         case META_LOCATE: locateReadDone(); break;
+         case META_LOCATELAST: locateLastReadDone(); break;
+         case META_SEEK: seekReadDone(); break;
+         case META_READ: rmetadataReadDone(); break;
+         case META_IDLE: readContinue(); break;
+         }                                         
+  }
+
+  event void At45db.computeCrcDone(error_t error, uint16_t crc) {
+    if (client != NO_CLIENT)
+      if (error != SUCCESS)
+       endRequest(FAIL);
+      else
+       switch (metaState)
+         {
+         case META_LOCATEFIRST: locateFirstCrcDone(crc); break;
+         case META_LOCATE: locateCrcDone(crc); break;
+         case META_LOCATELAST: locateLastCrcDone(crc); break;
+         case META_SEEK: seekCrcDone(crc); break;
+         case META_WRITE: wmetadataCrcDone(crc); break;
+         case META_READ: rmetadataCrcDone(crc); break;
+         }
+  }
+
+  event void At45db.copyPageDone(error_t error) { }
+
+  default event void LogWrite.appendDone[uint8_t logId](void* buf, storage_len_t l, bool rLost, error_t error) { }
+  default event void LogWrite.eraseDone[uint8_t logId](error_t error) { }
+  default event void LogWrite.syncDone[uint8_t logId](error_t error) { }
+  default event void LogRead.readDone[uint8_t logId](void* buf, storage_len_t l, error_t error) { }
+  default event void LogRead.seekDone[uint8_t logId](error_t error) {}
+
+  default command at45page_t At45dbVolume.remap[uint8_t logId](at45page_t volumePage) {return 0;}
+  default command at45page_t At45dbVolume.volumeSize[uint8_t logId]() {return 0;}
+  default async command error_t Resource.request[uint8_t logId]() {return SUCCESS;}
+  default async command error_t Resource.release[uint8_t logId]() { return FAIL; }
+}
diff --git a/tos/platforms/mulle/fix/README b/tos/platforms/mulle/fix/README
new file mode 100644 (file)
index 0000000..5c150d7
--- /dev/null
@@ -0,0 +1,12 @@
+This folder contains fixes in system files and chips that are currently not committed to the source tree, are Mulle specific or doesn't have any good or general solution. Any file put in this folder should be commented in this file; the reason the file is here should be stated and a plan how to fix (be able to remove it from here) the problem should be outlined.
+
+The files here need to be overlooked regularly to include changes made in the original file.
+
+The intention is to never have any files in this folder and if a file is put in this folder there should be a very good reason for it!
+
+
+------FILES------:
+
+tos/chips/at45db/At45dbP.nc: After a erase is done the buffer should be invalidated in some way. In the fix the buffer page member is set to AT45_MAX_PAGES, because the indexes of the pages are 0-(AT45_MAX_PAGES-1) thus AT45_MAX_PAGES indicates an invalid page number. Have spoken to David Gay about this. No fix seems to be committed, commit it our selfs?
+
+tos/chips/at45db/LogStorageP.nc: in struct pageinfo there is a member lastRecordOffset which is a uint8_t in the original file. Mulle and platforms using At45DB chips with 10 bit offset addresses need a larger storage unit than uint8_t and is thus changed to uint16_t. This should maybe be typedefed in every platform specific implementation of the at45db. We cant just change it to uint16_t because that would break backwards compatibility for old platforms. Have spoken with David Gay about this to, but nothing has happened.