#include "main.h"

#define USB_CON_SENSE_PIN PIN_D4

#include <pic18_usb.h>
#include "usbconfig.h"
#include <usb.h>

#include <usb.c>


/* commands from USB, must e.g. match command ids in kernel driver */
#define CMD_ECHO       0
#define CMD_GET_FUNC   1
#define CMD_SET_DELAY  2
#define CMD_GET_STATUS 3

#define CMD_I2C_IO     4
#define CMD_I2C_BEGIN  1  // flag fo I2C_IO
#define CMD_I2C_END    2  // flag fo I2C_IO

/* linux kernel flags */
#define I2C_M_TEN      0x10   /* we have a ten bit chip address */
#define I2C_M_RD      0x01
#define I2C_M_NOSTART      0x4000
#define I2C_M_REV_DIR_ADDR   0x2000
#define I2C_M_IGNORE_NAK   0x1000
#define I2C_M_NO_RD_ACK      0x0800

/* To determine what functionality is present */
#define I2C_FUNC_I2C         0x00000001
#define I2C_FUNC_10BIT_ADDR      0x00000002
#define I2C_FUNC_PROTOCOL_MANGLING   0x00000004 /* I2C_M_{REV_DIR_ADDR,NOSTART,..} */
#define I2C_FUNC_SMBUS_HWPEC_CALC   0x00000008 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC  0x00000800 /* SMBus 2.0 */ 
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC 0x00001000 /* SMBus 2.0 */ 
#define I2C_FUNC_SMBUS_PROC_CALL_PEC   0x00002000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC 0x00004000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL   0x00008000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_QUICK      0x00010000 
#define I2C_FUNC_SMBUS_READ_BYTE   0x00020000 
#define I2C_FUNC_SMBUS_WRITE_BYTE   0x00040000 
#define I2C_FUNC_SMBUS_READ_BYTE_DATA   0x00080000 
#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA   0x00100000 
#define I2C_FUNC_SMBUS_READ_WORD_DATA   0x00200000 
#define I2C_FUNC_SMBUS_WRITE_WORD_DATA   0x00400000 
#define I2C_FUNC_SMBUS_PROC_CALL   0x00800000 
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA   0x01000000 
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA 0x02000000 
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK   0x04000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK   0x08000000 /* w/ 1-byte reg. addr. */
#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2    0x10000000 /* I2C-like block xfer  */
#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2 0x20000000 /* w/ 2-byte reg. addr. */
#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC  0x40000000 /* SMBus 2.0 */
#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC 0x80000000 /* SMBus 2.0 */

#define I2C_FUNC_SMBUS_BYTE I2C_FUNC_SMBUS_READ_BYTE | \
                            I2C_FUNC_SMBUS_WRITE_BYTE
#define I2C_FUNC_SMBUS_BYTE_DATA I2C_FUNC_SMBUS_READ_BYTE_DATA | \
                                 I2C_FUNC_SMBUS_WRITE_BYTE_DATA
#define I2C_FUNC_SMBUS_WORD_DATA I2C_FUNC_SMBUS_READ_WORD_DATA | \
                                 I2C_FUNC_SMBUS_WRITE_WORD_DATA
#define I2C_FUNC_SMBUS_BLOCK_DATA I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
                                  I2C_FUNC_SMBUS_WRITE_BLOCK_DATA
#define I2C_FUNC_SMBUS_I2C_BLOCK I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
                                  I2C_FUNC_SMBUS_WRITE_I2C_BLOCK

#define I2C_FUNC_SMBUS_EMUL I2C_FUNC_SMBUS_QUICK | \
                            I2C_FUNC_SMBUS_BYTE | \
                            I2C_FUNC_SMBUS_BYTE_DATA | \
                            I2C_FUNC_SMBUS_WORD_DATA | \
                            I2C_FUNC_SMBUS_PROC_CALL | \
                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
                            I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
                            I2C_FUNC_SMBUS_I2C_BLOCK

/* ------------------------------------------------------------------------- */
#define DEFAULT_DELAY 10  // default 10us (100khz)
static unsigned clock_delay  = DEFAULT_DELAY;
static unsigned clock_delay2 = DEFAULT_DELAY/2;

static unsigned short expected;
static unsigned char saved_cmd;

unsigned int8 control_data[8];

struct i2c_cmd {
  unsigned char type;
  unsigned char cmd;
  unsigned short flags;
  unsigned short addr;
  unsigned short len;  
};

#define STATUS_IDLE          0
#define STATUS_ADDRESS_ACK   1
#define STATUS_ADDRESS_NAK   2

unsigned int8 status = STATUS_IDLE;

void main()
{

unsigned int8 replyBuf[4];

   setup_adc_ports(NO_ANALOGS|VSS_VDD);
   setup_adc(ADC_CLOCK_DIV_2);
   setup_psp(PSP_DISABLED);
   setup_wdt(WDT_OFF);
   setup_timer_0(RTCC_INTERNAL);
   setup_timer_1(T1_DISABLED);
   setup_timer_2(T2_DISABLED,0,1);
   setup_timer_3(T3_DISABLED|T3_DIV_BY_1);
   setup_ccp1(CCP_OFF);
   setup_comparator(NC_NC_NC_NC);
   setup_vref(FALSE);

   usb_init();



   while (TRUE) {
      if (usb_enumerated()) 
      {
         usb_gets(0,control_data,8,100);

         switch(control_data[1])
         {
           case CMD_ECHO: // echo (for transfer reliability testing)
             replyBuf[0] = control_data[2];
             replyBuf[1] = control_data[3];
             usb_puts(0,replyBuf,2,50);
             break;
         
           case CMD_GET_FUNC:
             usb_puts(0,(I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL),32,50);
             break;
         
           case CMD_SET_DELAY:
             /* The delay function used delays 4 system ticks per cycle. */
             /* This gives 1/3us at 12Mhz per cycle. The delay function is */
             /* called twice per clock edge and thus four times per full cycle. */ 
             /* Thus it is called one time per edge with the full delay */ 
             /* value and one time with the half one. Resulting in */
             /* 2 * n * 1/3 + 2 * 1/2 n * 1/3 = n us. */
             clock_delay = *(unsigned short*)(control_data+2);
             if(!clock_delay) clock_delay = 1;
             clock_delay2 = clock_delay/2;
             if(!clock_delay2) clock_delay2 = 1;
             break;
         
           case CMD_I2C_IO:
           case CMD_I2C_IO + CMD_I2C_BEGIN:
           case CMD_I2C_IO                 + CMD_I2C_END:
           case CMD_I2C_IO + CMD_I2C_BEGIN + CMD_I2C_END:
             // these are only allowed as class transfers
         
//             return i2c_do((struct i2c_cmd*)data);
             break;
         
           case CMD_GET_STATUS:
             replyBuf[0] = status;
             usb_puts(0,replyBuf,1,50);
             break;
         
           default:
             // must not happen ...
             break;          
         }

         delay_ms(10);
      }
   }
}