plausible i2c sniffer functionality and some more experiments,
[tv_i2c_hack] / main.c
diff --git a/main.c b/main.c
index aa593da..fa0be2f 100644 (file)
--- a/main.c
+++ b/main.c
@@ -6,13 +6,64 @@
 #include <util/delay.h>
 #include "serial.h"
 
+#undef USE_PCINT
+
 enum {
        I2C_IDLE,
-       I2C_MASTER_SEND,
-       I2C_MASTER_RECV
+       I2C_MASTER_WRITE,
+       I2C_MASTER_READ
+};
+
+enum {
+       ST_INVALID                      = 0,
+       ST_UNKNOWN                      = 0xf8,
+       ST_START                        = 0x08,
+       ST_REP_START            = 0x10,
+       ST_SLA_W_ACK            = 0x18,
+       ST_SLA_W_NACK           = 0x20,
+       ST_WR_ACK                       = 0x28,
+       ST_WR_NACK                      = 0x30,
+       ST_ARBLOST                      = 0x38,
+       ST_SLA_R_ACK            = 0x40,
+       ST_SLA_R_NACK           = 0x48,
+       ST_RD_ACK                       = 0x50,
+       ST_RD_NACK                      = 0x58,
+       ST_MATCH_W                      = 0x60,
+       ST_ARBLOST_MATCH        = 0x68,
+       ST_GENMATCH                     = 0x70,
+       ST_ARBLOST_GENMATCH = 0x78,
+       ST_SLAVE_RD_ACK         = 0x80,
+       ST_SLAVE_RD_NACK        = 0x88,
+       ST_SLAVE_GENRD_ACK      = 0x90,
+       ST_SLAVE_GENRD_NACK     = 0x98,
+       ST_SLAVE_STARTSTOP      = 0xa0,
+       ST_SLAVE_SLA_WR_ACK     = 0xa8,
+       ST_SLAVE_ARBLOST_WR_ACK = 0xb0,
+       ST_SLAVE_WR_ACK         = 0xb8,
+       ST_SLAVE_WR_NACK        = 0xc0,
+       ST_SLAVE_LAST_WR_ACK = 0xc8
+};
+
+enum {
+       EV_DEBUG,
+       EV_START,
+       EV_STOP,
+       EV_SLA_W,
+       EV_SLA_R,
+       EV_DATA
+};
+
+static const char *evname[] = {
+       "DBG",
+       "STA",
+       "STO",
+       "A+W",
+       "A+R",
+       "DAT"
 };
 
 #define TWINT_BIT      (1 << TWINT)
+#define TWEA_BIT       (1 << TWEA)
 #define TWSTA_BIT      (1 << TWSTA)
 #define TWSTO_BIT      (1 << TWSTO)
 #define TWEN_BIT       (1 << TWEN)
@@ -21,6 +72,8 @@ enum {
 #define I2C_START()    (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
 #define I2C_STOP()     (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
 #define I2C_WRITE()    (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_READ_ACK() (TWCR = TWINT_BIT | TWEA_BIT | TWEN_BIT | TWIE_BIT)
+#define I2C_READ_NACK()        (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
 
 unsigned char i2c_addr, i2c_subaddr;
 volatile unsigned char i2c_mode;
@@ -32,12 +85,16 @@ int i2c_retry_delay;
 uint16_t evq[EVQ_SIZE];
 volatile int evq_wr, evq_rd;
 
-#define LOGNUM(x)      \
+#define LOGNUM(x)      EVLOG(EV_DEBUG, (x))
+
+#define EVLOG(ev, data)        \
        do { \
-               evq[evq_wr] = x; \
-               evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
-               if(evq_wr == evq_rd) { \
-                       evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
+               if(ev != EV_DEBUG || dbgmode) { \
+                       evq[evq_wr] = ((uint16_t)(ev) << 8) | (data); \
+                       evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
+                       if(evq_wr == evq_rd) { \
+                               evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
+                       } \
                } \
        } while(0)
 
@@ -45,16 +102,38 @@ volatile int evq_wr, evq_rd;
 static char input[64];
 static unsigned char inp_cidx;
 
-static unsigned char data[8];
+static unsigned char data[16];
+
+static void (*async_func)(void);
+
+static uint16_t dump_addr;
+static int dump_count;
+static int dbgmode;
+static int snooping;
 
 static void proc_cmd(char *input);
-void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data);
+static void printstat(void);
+static void printdump(void);
+
+void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata);
+void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size);
+void i2c_wait(void);
+void i2c_async(void (*donecb)(void));
+void i2c_check_async(void);
+void i2c_hold(void);
+void i2c_release(void);
+void i2c_snoop(int onoff);
+#ifndef USE_PCINT
+void snoop(void);
+#endif
 
 int main(void)
 {
+       uint16_t val;
+
        /* tri-state everything and disable pullups */
        DDRB = 1;       /* B0: activity LED */
-       DDRC = 0x20; /* SCL output so we can hold the clock low when disabling i2c */
+       DDRC = 0;       /* I2C pins as inputs */
        DDRD = 0;
        PORTB = 0;
        PORTC = 0;
@@ -62,11 +141,12 @@ int main(void)
 
        TWBR = 10;              /* 10 with 1x prescaler should make it about 100khz */
        TWSR = 0;               /* prescaler 1x */
-       TWAR = 0x8a;            /* let's monitor for commands going to the jungle */
+       TWAR = 0;
        TWAMR = 0;
-       TWCR = (1 << TWEN) | (1 << TWIE); /* enable i2c, don't ack, enable intr */
+       TWCR = 0;               /* I2C disabled by default */
 
        i2c_mode = I2C_IDLE;
+       i2c_snoop(0);   /* disable snooping by default */
 
        init_serial(38400);
        sei();
@@ -74,7 +154,17 @@ int main(void)
        printf("Starting i2c hack\n");
 
        for(;;) {
-               //uint16_t val = 0xffff;
+#ifndef USE_PCINT
+               if(snooping) {
+                       snoop();
+                       if(have_input()) {
+                               i2c_snoop(0);
+                       }
+                       continue;
+               }
+#endif
+
+               i2c_check_async();
 
                if(have_input()) {
                        int c = getchar();
@@ -91,7 +181,7 @@ int main(void)
                }
 
                /* read from queue and send over the serial port */
-               /*
+               val = 0xffff;
                cli();
                if(evq_wr != evq_rd) {
                        val = evq[evq_rd];
@@ -100,85 +190,168 @@ int main(void)
                sei();
 
                if(val != 0xffff) {
-                       printf("s(%u): %x\n", (unsigned int)(val >> 8), (unsigned int)(val & 0xff));
+                       unsigned char ev = val >> 8;
+                       if(ev > EV_DATA) {
+                               printf("%02x: %02x\n", ev, val & 0xff);
+                       } else {
+                               printf("%s: %02x\n", evname[ev], val & 0xff);
+                       }
                }
-               */
-               /*
-               _delay_us(100);
-               data[0] = 0x3f;
-               i2c_write(0x8a, 0x1c, 1, data);
-               */
        }
        return 0;
 }
 
 static void proc_cmd(char *input)
 {
-       if(strcmp(input, "rgb") == 0) {
-               printf("OK sending RGB switch command\n");
+       char *endp;
+
+       if(strcmp(input, "dbg") == 0) {
+               printf("OK dbg\n");
+               dbgmode = 1;
 
-               /* i2c addr 8b: jungle
-                *  subaddr 2b: Control 1 reg
-                *  bit 1: YUV
+       } else if(strcmp(input, "nodbg") == 0) {
+               printf("OK nodbg\n");
+               dbgmode = 0;
+
+       } else if(strcmp(input, "start") == 0) {
+               printf("OK snooping\n");
+               i2c_snoop(1);
+
+       } else if(strcmp(input, "stop") == 0) {
+               i2c_snoop(0);
+               printf("OK stopped snooping\n");
+
+       } else if(strcmp(input, "av") == 0) {
+               printf("OK AV\n");
+
+               /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
+               data[0] = 0x22;
+               i2c_write(0x8a, 0x22, data, 1);
+               i2c_async(i2c_hold);
+
+       } else if(strcmp(input, "rgb") == 0) {
+               printf("OK RGB\n");
+
+               /* Control 0 (2a): 0 IE2 RBL AKB  CL3 CL2 CL1 CL0
+                * Control 1 (2b): 0  0   0   0    0   0  YUV HBL
                 */
-               data[0] = 2;
-               i2c_write(0x8a, 0x2b, 1, data);
+               data[0] = 0x70;
+               data[1] = 0;
+               i2c_write(0x8a, 0x2a, data, 1);
+               while(i2c_mode);
+               i2c_write(0x8a, 0x2b, data + 1, 1);
 
        } else if(memcmp(input, "vol ", 4) == 0) {
                int vol = atoi(input + 4);
                if(vol < 1 || vol > 63) {
-                       printf("ERR invalid vol (%s)\n", input + 4);
+                       printf("ERR vol (%s)\n", input + 4);
                } else {
                        data[0] = vol;
-                       i2c_write(0x8a, 0x1f, 1, data);
+                       i2c_write(0x8a, 0x1f, data, 1);
                        printf("OK volume: %d\n", vol);
                }
 
-       } else if(strcmp(input, "zoom") == 0) {
-               data[0] = 0x3f;
-               i2c_write(0x8a, 0x10, 1, data);
-               printf("OK zoom in\n");
-
-       } else if(strcmp(input, "unzoom") == 0) {
-               data[0] = 0x20;
-               i2c_write(0x8a, 0x10, 1, data);
-               printf("OK unzoom\n");
-
        } else if(memcmp(input, "sat", 3) == 0) {
-               printf("OK saturate\n");
+               printf("OK sat\n");
                data[0] = atoi(input + 3);
                if(data[0] <= 0 || data[0] > 0x3f) {
                        data[0] = 0x1f;
                }
-               i2c_write(0x8a, 0x1c, 1, data);
-               while(i2c_mode);
-               TWCR = 0;
+               i2c_write(0x8a, 0x1c, data, 1);
+               i2c_async(i2c_hold);    /* hold I2C when done */
+
+       } else if(strcmp(input, "rel") == 0) {
+               printf("OK release\n");
+               i2c_release();
+
+       } else if(strcmp(input, "status") == 0) {
+               i2c_read(0x8a, 0, data, 3);
+               i2c_async(printstat);
+
+       } else if(memcmp(input, "rd ", 3) == 0) {
+               dump_addr = strtol(input + 3, &endp, 16);
+
+               if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
+                       dump_count = 1;
+                       i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
+                       i2c_async(printdump);
+               } else {
+                       printf("ERR address: %s\n", input + 3);
+               }
+
+       } else if(memcmp(input, "dump ", 5) == 0) {
+               dump_addr = strtol(input + 5, &endp, 16);
+
+               if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
+                       dump_count = 1;
+                       dump_addr &= 0xff0;
+                       i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
+                       i2c_async(printdump);
+               } else {
+                       printf("ERR address: %s\n", input + 5);
+               }
 
        } else if(strcmp(input, "abort") == 0) {
                if(i2c_mode != I2C_IDLE) {
                        i2c_mode = I2C_IDLE;
                        I2C_STOP();
-                       printf("OK aborting i2c op\n");
+                       printf("OK\n");
                } else {
                        printf("ERR i2c is idle\n");
                }
 
        } else {
-               printf("ERR invalid command (%s)\n", input);
+               printf("ERR command (%s)\n", input);
        }
 }
 
-void i2c_write(unsigned char addr, unsigned char subaddr, int ndata, unsigned char *data)
+static void printstat(void)
+{
+       printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
+                       (unsigned int)data[1], (unsigned int)data[2]);
+}
+
+static void printdump(void)
+{
+       int i;
+       while(dump_count > 0) {
+               printf("OK %03x:", dump_addr);
+               for(i=0; i<16; i++) {
+                       if(i == 8) putchar(' ');
+
+                       if(--dump_count >= 0) {
+                               printf(" %02x", data[i]);
+                       } else {
+                               printf("   ");
+                       }
+                       dump_addr += 16;
+               }
+               putchar('\n');
+       }
+}
+
+void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata)
 {
        i2c_addr = addr & 0xfe;
        i2c_subaddr = subaddr;
-       i2c_mode = I2C_MASTER_SEND;
+       i2c_mode = I2C_MASTER_WRITE;
        i2c_ndata = ndata;
        i2c_data = data;
        i2c_seq = 0;
        I2C_START();
 }
 
+void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size)
+{
+       i2c_addr = addr & 0xfe;
+       i2c_subaddr = subaddr;
+       i2c_mode = I2C_MASTER_READ;
+       i2c_ndata = size;
+       i2c_data = buf;
+       i2c_seq = 0;
+       I2C_START();
+}
+
 void i2c_handle_send(void)
 {
        unsigned char state = TWSR & 0xf8;
@@ -187,27 +360,25 @@ void i2c_handle_send(void)
        LOGNUM(state);
 
        switch(state) {
-       case 0x10:
+       case ST_REP_START:      /* 0x10 */
                /* repeated start, same as start, but also increment i2c_seq */
                i2c_seq++;
-       case 0x8:
+       case ST_START:  /* 0x8 */
                /* start initiated, write the slave address */
                //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
                TWDR = i2c_addr;
                I2C_WRITE();
                break;
 
-       case 0x18:
+       case ST_SLA_W_ACK:      /* 0x18 */
                /* slave addr sent and ACKed, send subaddr or data */
                if(i2c_seq == 0) {
                        /* this is the first packet, send subaddr */
                        i2c_seq++;
-                       LOGNUM(0x100 | i2c_subaddr);
                        TWDR = i2c_subaddr;
                        I2C_WRITE();
                } else {
                        if(i2c_ndata--) {
-                               LOGNUM(0x200 | *i2c_data);
                                TWDR = *i2c_data++;
                                I2C_WRITE();
                        } else {
@@ -218,14 +389,14 @@ void i2c_handle_send(void)
                }
                break;
 
-       case 0x20:
+       case ST_SLA_W_NACK:     /* 0x20 */
                /* slave addr sent but not ACKed, abort */
                i2c_mode = I2C_IDLE;
                I2C_STOP();
                printf("i2c: NACK after SLA+W\n");
                break;
 
-       case 0x28:
+       case ST_WR_ACK: /* 0x28 */
                /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
 #if 0
                if(i2c_seq == 0) {
@@ -235,7 +406,6 @@ void i2c_handle_send(void)
 #endif
                        /* data was sent, send more data or stop */
                        if(i2c_ndata--) {
-                               LOGNUM(0x200 | *i2c_data);
                                TWDR = *i2c_data++;
                                I2C_WRITE();
                        } else {
@@ -245,7 +415,7 @@ void i2c_handle_send(void)
 //             }
                break;
 
-       case 0x30:
+       case ST_WR_NACK:        /* 0x30 */
                /* data (or subaddr) sent but not ACKed */
                if(i2c_seq == 0) {
                        /* NACK after subaddr, abort */
@@ -253,20 +423,20 @@ void i2c_handle_send(void)
                } else {
                        /* NACK after data */
                        if(i2c_ndata) {
-                               printf("i2c: NACK with %d data packets pending\n", i2c_ndata);
+                               printf("i2c: NACK with %d pending\n", i2c_ndata);
                        }
                }
                i2c_mode = I2C_IDLE;
                I2C_STOP();
                break;
 
-       case 0x38:
+       case ST_ARBLOST:        /* 0x38 */
                /* arbitration lost */
-               printf("i2c: arbitration lost\n");
+               printf("i2c: arb lost\n");
                I2C_START();
                break;
 
-       case 0:
+       case ST_INVALID:        /* 0 */
                printf("i2c: invalid start/stop\n");
                i2c_mode = I2C_IDLE;
                I2C_STOP();
@@ -281,46 +451,252 @@ void i2c_handle_send(void)
 
 void i2c_handle_recv(void)
 {
-       printf("i2c: recv unimplemented\n");
+       unsigned char state = TWSR & 0xf8;
+
+       LOGNUM(state);
+
+       switch(state) {
+       case ST_START:          /* 0x8 */
+               TWDR = i2c_addr;        /* start a *write* (we need to send the subaddress before reading) */
+               I2C_WRITE();
+               break;
+
+       case ST_REP_START:      /* 0x10 */
+               /* repeated start, now we can issue the actual read */
+               TWDR = i2c_addr | 1;
+               I2C_WRITE();
+               break;
+
+       case ST_SLA_W_ACK:      /* 0x18 */
+               /* SLA+W means we just started the write part, need to send the subaddress */
+               TWDR = i2c_subaddr;
+               I2C_WRITE();
+               break;
+
+       case ST_SLA_W_NACK:
+               /* slave addr sent but not ACKed, abort */
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+               printf("i2c: NACK after SLA+W (R)\n");
+               break;
+
+       case ST_WR_ACK:         /* 0x28 */
+               /* the subaddress write was ACKed, rep-start to issue the read */
+               I2C_START();
+               break;
+
+       case ST_WR_NACK:
+               /* the subaddress write was not ACKed, abort */
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+               printf("i2c: NACK after subaddr (R)\n");
+               break;
+
+       case ST_SLA_R_ACK:      /* 0x40 */
+               /* SLA+R was ACKed send ACK to start receiving */
+               I2C_READ_ACK();
+               break;
+
+       case ST_RD_ACK:         /* 0x50 */
+               /* ... or last read was ACKed, again read next and ACK for more */
+               *i2c_data++ = TWDR;
+               if(--i2c_ndata > 0) {
+                       I2C_READ_ACK();
+               } else {
+                       I2C_READ_NACK();
+               }
+               break;
+
+       case ST_SLA_R_NACK:     /* 0x48 */
+               /* SLA+R was sent but ACK was not received, abort */
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+               printf("i2c: NACK after SLA+R\n");
+               break;
+
+       case ST_RD_NACK:        /* 0x58 */
+               /* read without ACK, we get this after we send a NACK, or this is the last byte (?) */
+               if(i2c_ndata > 0) {
+                       *i2c_data++ = TWDR;
+                       i2c_ndata--;
+               }
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+               break;
+
+       case ST_ARBLOST:        /* 0x38 */
+               /* arbitration lost */
+               printf("i2c: arb lost\n");
+               I2C_START();
+               break;
+
+       case ST_INVALID:        /* 0 */
+               printf("i2c: invalid start/stop\n");
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+               break;
+
+       default:
+               printf("i2c: unexpected state (R): 0x%x\n", state);
+               i2c_mode = I2C_IDLE;
+               I2C_STOP();
+       }
 }
 
 ISR(TWI_vect)
 {
-       uint16_t ev;
-
        switch(i2c_mode) {
-       case I2C_MASTER_SEND:
+       case I2C_MASTER_WRITE:
                i2c_handle_send();
                break;
 
-       case I2C_MASTER_RECV:
+       case I2C_MASTER_READ:
                i2c_handle_recv();
                break;
+       }
+}
 
-       default:
-               /* log traffic on the bus */
-               ev = (uint16_t)(TWSR & 0xf8) << 8;
-               switch(TWSR & 0xf8) {
-               case 0x80:      /* own SLA+W received, ack */
-               case 0x88:      /* own SLA+W recv, no-ack */
-               case 0x90:      /* general recv, ack */
-               case 0x98:      /* general recv, no-ack */
-                       ev |= TWDR;
-                       break;
-
-               default:
-                       break;
-               }
+void i2c_wait(void)
+{
+       while(i2c_mode);
+}
+
+void i2c_async(void (*donecb)(void))
+{
+       async_func = donecb;
+}
+
+void i2c_check_async(void)
+{
+       if(async_func && i2c_mode == I2C_IDLE) {
+               async_func();
+               async_func = 0;
+       }
+}
+
+void i2c_hold(void)
+{
+       TWCR = 0;               /* make sure the AVR i2c hardware is disabled */
+       DDRC = 0x20;    /* ... and drive SCL pin low */
+       PORTC = 0;
+}
+
+void i2c_release(void)
+{
+       DDRC = 0;
+}
+
+#define PC_SCL 5
+#define PC_SDA 4
+#define PC_SCL_BIT     (1 << PC_SCL)
+#define PC_SDA_BIT     (1 << PC_SDA)
 
-               /* append data to the event queue */
-               TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT;
+static unsigned char pcprev, state;
+static unsigned char value;
+static int nbits;
 
-               evq[evq_wr] = ev;
-               evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1);
-               if(evq_wr == evq_rd) {
+/* use PCINT12,13 to snoop i2c traffic */
+void i2c_snoop(int onoff)
+{
+       snooping = onoff;
+
+       if(!onoff) {
+#ifdef USE_PCINT
+               /* to stop snooping just disable the interrupt */
+               cli();
+               PCMSK1 = 0;
+               PCICR &= ~(1 << PCIE1);
+               sei();
+#endif
+               return;
+       }
+
+       TWCR = 0;               /* make sure the i2c hw is disabled */
+       DDRC = 0;
+       PORTC = 0;
+
+#ifdef USE_PCINT
+       cli();
+       PCICR |= 1 << PCIE1;    /* enable the pin-change interrupt */
+       PCMSK1 = (1 << PCINT12) | (1 << PCINT13);
+#endif
+
+       pcprev = PINC;
+       state = 0;
+       value = 0;
+       nbits = 0;
+#ifdef USE_PCINT
+       sei();
+#endif
+}
+
+#ifdef USE_PCINT
+ISR(PCINT1_vect)
+#else
+void snoop(void)
+#endif
+{
+       unsigned char pinc, delta;
+
+       pinc = PINC;
+       delta = pinc ^ pcprev;
+       pcprev = pinc;
+
+       if(delta & PC_SDA_BIT) {
+               if(pinc & PC_SCL_BIT) {
+                       /* clock is high, so this is either a start or a stop */
+                       if(pinc & PC_SDA_BIT) {
+                               EVLOG(EV_STOP, 0);
+                               state = 0;
+                       } else {
+                               EVLOG(EV_START, 0);
+                               state = EV_START;
+                               value = 0;
+                               nbits = 0;
+                       }
+                       return;
+               }
+       }
+
+       if(!state) {
+               uint16_t val, ev;
+               if(evq_wr != evq_rd) {
+                       val = evq[evq_rd];
                        evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
+                       ev = val >> 8;
+                       if(ev <= EV_STOP) {
+                               printf("%s\n", evname[ev]);
+                       } else {
+                               printf("%s: %02x\n", evname[ev], val & 0xff);
+                       }
                }
+               return;
+       }
 
-               PORTB = 1;
+       if(delta & PC_SCL_BIT) {
+               if(pinc & PC_SCL_BIT) {
+                       /* clock is going high, shift SDA */
+                       value = (value << 1) | ((pinc >> PC_SDA) & 1);
+                       if(++nbits >= 8) {
+                               switch(state) {
+                               case EV_START:
+                                       state = (value & 1) ? EV_SLA_R : EV_SLA_W;
+                                       EVLOG(state, value & 0xfe);
+                                       break;
+                               case EV_SLA_W:
+                               case EV_SLA_R:
+                                       state = EV_DATA;
+                                       EVLOG(state, value);
+                               case EV_DATA:
+                                       //EVLOG(state, value);
+                               default:
+                                       break;
+                               }
+                               nbits = 0;
+                               value = 0;
+                       }
+               }
        }
+
+       PORTB ^= 1;
 }