⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 at45dbp.nc

📁 tinyos-2.x.rar
💻 NC
字号:
// $Id: At45dbP.nc,v 1.10 2008/06/23 20:25:15 regehr Exp $

/*
 * "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 = reqPage;
    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);
  }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -