/*********************************  I2Cgate.h  *******************************
 * $Source: /home/cvs/ESP/gen2/software/msp430/include/I2Cgate.h,v $
 *  Copyright (C) 2003 MBARI
 *
 *  MBARI Proprietary Information. All rights reserved.
 * $Id: I2Cgate.h,v 1.14 2004/06/03 17:55:16 brent Exp $
 *
 * I2C gateway message parsing and processing
 *    in a "sort of" object oriented fashion
 *
 *****************************************************************************/

#ifndef I2Cgate_h
#define I2Cgate_h

#include "parser.h"     //this extends generic message parsing
#include "msp430.h"

#define I2Cclock  0x08    //special function bits in port 3
#define I2Cdata   0x02

typedef const struct I2CgateInstance I2CgateInstance;

typedef void I2Cisr (I2CgateInstance *instance);

typedef I2Cisr * const* I2Cvectors; 

struct I2CgateInstance {
  struct parserInstance parser;  //extends parserInstance
  I2Cvectors *I2CisrState;  //I2C/USART0 interrupt processing state
  char *description;        //ASCII application description string
  int                   //process application specific commands from host
    (*localCmd) (I2CgateInstance *instance, unsigned cmd, unsigned paramLen);
  byte     lowPeriod, highPeriod;  //MCLKS per I2C low and high clock periods
};

//read-only for gateway app (host must initialize this)
struct I2CgateConfig {  //gateway configuration
  byte  retries;    //# of times to retry write if CRC fails or NACK received
                    //MSB should be cleared
  byte  timeout;    //bus timeout in quarters of seconds, 0 if none
  byte  address;    //gateway's own address on the I2C bus or zero if none
                    //MSB set if gateway should ignore general calls
  byte  OK;         //if !=0, send (& 0x7f) to host to confirm nonCRC writes
                    //MSB set to enable appending this to received nonCRC writes
  byte  busNACK;    //if !=0, byte to send if bus times out
                    //MSB set to enable sending I2CgateHostRead notifications
  byte  adrNACK;    //if !=0, send if slave does not acknowledge its address
                    //MSB set to suppress testing address if unchanged
  byte  slaveNACK;  //if !=0, send if slave NACKs a data byte written to it
                    //MSB set to suppress reporting null writes
  byte  masterNACK; //if !=0, send if master NACKs a byte it requested 
                    //MSB set for 1 bit message CRC ACK, clear for full byte ACK
  byte  CRCOK;      //if !=0, send to confirm slave received CRC checked message
                    //MSB set to enable appending this to received CRC writes
  byte  CRCNACK;    //if !=0, send if slave NACKs CRC checked message
                    //MSB set to enable appending this to received CRC writes
  byte  maxAhead;   //# bytes to accumulate before I2C writes begin
};


#ifndef BUILDING_I2CGATE
extern
#endif
 struct {  //private to I2C gateway
  I2Cisr *rcvISR; //if non-null, call this ISR after next host byte received
  I2Cisr *xmtISR; //if non-null, call this ISR after next byte is sent to host
} I2CgateDefer;  


// Initialize the parser / message processor
void I2CgateInit (I2CgateInstance *instance);

// (Re-)Start the main thread of the command parser
//  interrupts should be disabled on entry, they are enabled upon return
void I2CgateRestart (I2CgateInstance *instance);

//process commands until a syntax error found
// returns -1 if the null command was parsed.
//  other codes come from the instance's command parser
int I2CgateParseCmds (I2CgateInstance *instance)

/*
  process next command given its first byte
  returns 0 if all went well, -1 for the null command
    other return codes come from the app's local command processor
*/
int I2CgateParseNextCmd (I2CgateInstance *instance, unsigned adrOrCmdLen);



// The following arbitrates access to the host serial output fifo:
// wait to complete the current message and hold off processing more
void I2CgateHold (I2CgateInstance *instance);

// call once per Timer tic (TimerHZ/sec)
void I2CgateTic (I2CgateInstance *instance);

// call when dwarf CPLD's I2C clock counter decriments to zero
void I2CgateCkHeld (I2CgateInstance *instance);


static inline void connectI2C (void)
{
  set8 (P3SEL, I2Cclock|I2Cdata);  //allow controller to drive SDA & SCL
}

// when an interesting I2C clock edge occurs
static inline void I2CgateCkEdge (I2CgateInstance *instance)
{
  connectI2C();  //reattach controller, may signal STOP
  clear8 (DWRFIRQIE, DWRFI2CSCL);
}

// The following arbitrates access to the host serial output fifo:
// wait to complete the current message allow access to output fifo
void I2CgateHold (I2CgateInstance *instance);

// disallow access to output fifo and resume processing I2C messages
static void I2CgateRelease (I2CgateInstance *instance);


//retry a previously deferred ISR
void I2CretryXmtISR (I2CgateInstance *instance);
void I2CretryRcvISR (I2CgateInstance *instance);

// resume processing I2C messages via interrupt
// if they had been held due to a nearly full serial output fifo
// call with interrupts disabled
#define I2CgateOutResume(instance) \
  {if (I2CgateDefer.xmtISR) I2CretryXmtISR(instance);}


// resume processing I2C messages via interrupt
// if they had been held due to an empty serial input fifo
// call with interrupts disabled
#define I2CgateInResume(instance) \
  {if (I2CgateDefer.rcvISR) I2CretryRcvISR(instance);}


// screen USART input errors.
// framework will restart if this returns true
boolean I2CgateInErr (I2CgateInstance *instance, byte USARTerrMask);

#endif
