#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 data[64];
unsigned int8 replyBuf[4];
unsigned int8 i=0;

   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);

   printf("MLAB I2C to USB adapter started \n\r ");

         output_high(PIN_B4);

   usb_init();
   usb_wait_for_enumeration();   


   printf("device enumerated \n\r ");

if(usb_tbe(0)) printf(" endpoint 0 ready \n\r ");
else printf(" endpoint 0 disabled \n\r ");
if(usb_tbe(1)) printf(" endpoint 1 ready \n\r ");
else printf(" endpoint 1 disabled \n\r ");
if(usb_tbe(2)) printf(" endpoint 2 ready \n\r ");
else printf(" endpoint 2 disabled \n\r ");

if(usb_puts(0,data,64,1000)) printf(" endpoint 0 inicialized \n\r ");
else printf("cannot write to endpoint 0 \n\r ");

   while (TRUE) {
      if (usb_enumerated()) 
      {

      output_low(PIN_B4);

/*         if(usb_kbhit(0))
         {
            printf("control data received...  \n\r");
   
            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:
                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;          
            }
         }*/

         if(usb_kbhit(i))
         {
           printf(" data received at endpoint %d \n\r ", i);
           usb_gets(i,data,64,1000);
         }

         delay_ms(10);

         output_high(PIN_B4);
         if( i==1 )i=0;
         else i=1;
      }
   }
}