plausible i2c sniffer functionality and some more experiments,
[tv_i2c_hack] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include <util/delay.h>
7 #include "serial.h"
8
9 #undef USE_PCINT
10
11 enum {
12         I2C_IDLE,
13         I2C_MASTER_WRITE,
14         I2C_MASTER_READ
15 };
16
17 enum {
18         ST_INVALID                      = 0,
19         ST_UNKNOWN                      = 0xf8,
20         ST_START                        = 0x08,
21         ST_REP_START            = 0x10,
22         ST_SLA_W_ACK            = 0x18,
23         ST_SLA_W_NACK           = 0x20,
24         ST_WR_ACK                       = 0x28,
25         ST_WR_NACK                      = 0x30,
26         ST_ARBLOST                      = 0x38,
27         ST_SLA_R_ACK            = 0x40,
28         ST_SLA_R_NACK           = 0x48,
29         ST_RD_ACK                       = 0x50,
30         ST_RD_NACK                      = 0x58,
31         ST_MATCH_W                      = 0x60,
32         ST_ARBLOST_MATCH        = 0x68,
33         ST_GENMATCH                     = 0x70,
34         ST_ARBLOST_GENMATCH = 0x78,
35         ST_SLAVE_RD_ACK         = 0x80,
36         ST_SLAVE_RD_NACK        = 0x88,
37         ST_SLAVE_GENRD_ACK      = 0x90,
38         ST_SLAVE_GENRD_NACK     = 0x98,
39         ST_SLAVE_STARTSTOP      = 0xa0,
40         ST_SLAVE_SLA_WR_ACK     = 0xa8,
41         ST_SLAVE_ARBLOST_WR_ACK = 0xb0,
42         ST_SLAVE_WR_ACK         = 0xb8,
43         ST_SLAVE_WR_NACK        = 0xc0,
44         ST_SLAVE_LAST_WR_ACK = 0xc8
45 };
46
47 enum {
48         EV_DEBUG,
49         EV_START,
50         EV_STOP,
51         EV_SLA_W,
52         EV_SLA_R,
53         EV_DATA
54 };
55
56 static const char *evname[] = {
57         "DBG",
58         "STA",
59         "STO",
60         "A+W",
61         "A+R",
62         "DAT"
63 };
64
65 #define TWINT_BIT       (1 << TWINT)
66 #define TWEA_BIT        (1 << TWEA)
67 #define TWSTA_BIT       (1 << TWSTA)
68 #define TWSTO_BIT       (1 << TWSTO)
69 #define TWEN_BIT        (1 << TWEN)
70 #define TWIE_BIT        (1 << TWIE)
71
72 #define I2C_START()     (TWCR = TWINT_BIT | TWSTA_BIT | TWEN_BIT | TWIE_BIT)
73 #define I2C_STOP()      (TWCR = TWINT_BIT | TWSTO_BIT | TWEN_BIT | TWIE_BIT)
74 #define I2C_WRITE()     (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
75 #define I2C_READ_ACK()  (TWCR = TWINT_BIT | TWEA_BIT | TWEN_BIT | TWIE_BIT)
76 #define I2C_READ_NACK() (TWCR = TWINT_BIT | TWEN_BIT | TWIE_BIT)
77
78 unsigned char i2c_addr, i2c_subaddr;
79 volatile unsigned char i2c_mode;
80 int i2c_seq, i2c_ndata;
81 unsigned char *i2c_data;
82 int i2c_retry_delay;
83
84 #define EVQ_SIZE        64
85 uint16_t evq[EVQ_SIZE];
86 volatile int evq_wr, evq_rd;
87
88 #define LOGNUM(x)       EVLOG(EV_DEBUG, (x))
89
90 #define EVLOG(ev, data) \
91         do { \
92                 if(ev != EV_DEBUG || dbgmode) { \
93                         evq[evq_wr] = ((uint16_t)(ev) << 8) | (data); \
94                         evq_wr = (evq_wr + 1) & (EVQ_SIZE - 1); \
95                         if(evq_wr == evq_rd) { \
96                                 evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1); \
97                         } \
98                 } \
99         } while(0)
100
101 /* serial input buffer */
102 static char input[64];
103 static unsigned char inp_cidx;
104
105 static unsigned char data[16];
106
107 static void (*async_func)(void);
108
109 static uint16_t dump_addr;
110 static int dump_count;
111 static int dbgmode;
112 static int snooping;
113
114 static void proc_cmd(char *input);
115 static void printstat(void);
116 static void printdump(void);
117
118 void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata);
119 void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size);
120 void i2c_wait(void);
121 void i2c_async(void (*donecb)(void));
122 void i2c_check_async(void);
123 void i2c_hold(void);
124 void i2c_release(void);
125 void i2c_snoop(int onoff);
126 #ifndef USE_PCINT
127 void snoop(void);
128 #endif
129
130 int main(void)
131 {
132         uint16_t val;
133
134         /* tri-state everything and disable pullups */
135         DDRB = 1;       /* B0: activity LED */
136         DDRC = 0;       /* I2C pins as inputs */
137         DDRD = 0;
138         PORTB = 0;
139         PORTC = 0;
140         PORTD = 0;
141
142         TWBR = 10;              /* 10 with 1x prescaler should make it about 100khz */
143         TWSR = 0;               /* prescaler 1x */
144         TWAR = 0;
145         TWAMR = 0;
146         TWCR = 0;               /* I2C disabled by default */
147
148         i2c_mode = I2C_IDLE;
149         i2c_snoop(0);   /* disable snooping by default */
150
151         init_serial(38400);
152         sei();
153
154         printf("Starting i2c hack\n");
155
156         for(;;) {
157 #ifndef USE_PCINT
158                 if(snooping) {
159                         snoop();
160                         if(have_input()) {
161                                 i2c_snoop(0);
162                         }
163                         continue;
164                 }
165 #endif
166
167                 i2c_check_async();
168
169                 if(have_input()) {
170                         int c = getchar();
171                         putchar(c);
172
173                         if(c == '\r' || c == '\n') {
174                                 putchar('\n');
175                                 input[inp_cidx] = 0;
176                                 proc_cmd(input);
177                                 inp_cidx = 0;
178                         } else if(inp_cidx < sizeof input - 1) {
179                                 input[inp_cidx++] = c;
180                         }
181                 }
182
183                 /* read from queue and send over the serial port */
184                 val = 0xffff;
185                 cli();
186                 if(evq_wr != evq_rd) {
187                         val = evq[evq_rd];
188                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
189                 }
190                 sei();
191
192                 if(val != 0xffff) {
193                         unsigned char ev = val >> 8;
194                         if(ev > EV_DATA) {
195                                 printf("%02x: %02x\n", ev, val & 0xff);
196                         } else {
197                                 printf("%s: %02x\n", evname[ev], val & 0xff);
198                         }
199                 }
200         }
201         return 0;
202 }
203
204 static void proc_cmd(char *input)
205 {
206         char *endp;
207
208         if(strcmp(input, "dbg") == 0) {
209                 printf("OK dbg\n");
210                 dbgmode = 1;
211
212         } else if(strcmp(input, "nodbg") == 0) {
213                 printf("OK nodbg\n");
214                 dbgmode = 0;
215
216         } else if(strcmp(input, "start") == 0) {
217                 printf("OK snooping\n");
218                 i2c_snoop(1);
219
220         } else if(strcmp(input, "stop") == 0) {
221                 i2c_snoop(0);
222                 printf("OK stopped snooping\n");
223
224         } else if(strcmp(input, "av") == 0) {
225                 printf("OK AV\n");
226
227                 /* AV switch (22): 0 0 SVO CMB1 CMB0 INA INB 0 */
228                 data[0] = 0x22;
229                 i2c_write(0x8a, 0x22, data, 1);
230                 i2c_async(i2c_hold);
231
232         } else if(strcmp(input, "rgb") == 0) {
233                 printf("OK RGB\n");
234
235                 /* Control 0 (2a): 0 IE2 RBL AKB  CL3 CL2 CL1 CL0
236                  * Control 1 (2b): 0  0   0   0    0   0  YUV HBL
237                  */
238                 data[0] = 0x70;
239                 data[1] = 0;
240                 i2c_write(0x8a, 0x2a, data, 1);
241                 while(i2c_mode);
242                 i2c_write(0x8a, 0x2b, data + 1, 1);
243
244         } else if(memcmp(input, "vol ", 4) == 0) {
245                 int vol = atoi(input + 4);
246                 if(vol < 1 || vol > 63) {
247                         printf("ERR vol (%s)\n", input + 4);
248                 } else {
249                         data[0] = vol;
250                         i2c_write(0x8a, 0x1f, data, 1);
251                         printf("OK volume: %d\n", vol);
252                 }
253
254         } else if(memcmp(input, "sat", 3) == 0) {
255                 printf("OK sat\n");
256                 data[0] = atoi(input + 3);
257                 if(data[0] <= 0 || data[0] > 0x3f) {
258                         data[0] = 0x1f;
259                 }
260                 i2c_write(0x8a, 0x1c, data, 1);
261                 i2c_async(i2c_hold);    /* hold I2C when done */
262
263         } else if(strcmp(input, "rel") == 0) {
264                 printf("OK release\n");
265                 i2c_release();
266
267         } else if(strcmp(input, "status") == 0) {
268                 i2c_read(0x8a, 0, data, 3);
269                 i2c_async(printstat);
270
271         } else if(memcmp(input, "rd ", 3) == 0) {
272                 dump_addr = strtol(input + 3, &endp, 16);
273
274                 if(endp > input + 3 && dump_addr >= 0 && dump_addr < 2048) {
275                         dump_count = 1;
276                         i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 1);
277                         i2c_async(printdump);
278                 } else {
279                         printf("ERR address: %s\n", input + 3);
280                 }
281
282         } else if(memcmp(input, "dump ", 5) == 0) {
283                 dump_addr = strtol(input + 5, &endp, 16);
284
285                 if(endp > input + 5 && dump_addr >= 0 && dump_addr < 2048) {
286                         dump_count = 1;
287                         dump_addr &= 0xff0;
288                         i2c_read(0xa0 | ((dump_addr >> 7) & 0xe), dump_addr & 0xff, data, 16);
289                         i2c_async(printdump);
290                 } else {
291                         printf("ERR address: %s\n", input + 5);
292                 }
293
294         } else if(strcmp(input, "abort") == 0) {
295                 if(i2c_mode != I2C_IDLE) {
296                         i2c_mode = I2C_IDLE;
297                         I2C_STOP();
298                         printf("OK\n");
299                 } else {
300                         printf("ERR i2c is idle\n");
301                 }
302
303         } else {
304                 printf("ERR command (%s)\n", input);
305         }
306 }
307
308 static void printstat(void)
309 {
310         printf("OK status: %02x %02x %02x\n", (unsigned int)data[0],
311                         (unsigned int)data[1], (unsigned int)data[2]);
312 }
313
314 static void printdump(void)
315 {
316         int i;
317         while(dump_count > 0) {
318                 printf("OK %03x:", dump_addr);
319                 for(i=0; i<16; i++) {
320                         if(i == 8) putchar(' ');
321
322                         if(--dump_count >= 0) {
323                                 printf(" %02x", data[i]);
324                         } else {
325                                 printf("   ");
326                         }
327                         dump_addr += 16;
328                 }
329                 putchar('\n');
330         }
331 }
332
333 void i2c_write(unsigned char addr, unsigned char subaddr, unsigned char *data, int ndata)
334 {
335         i2c_addr = addr & 0xfe;
336         i2c_subaddr = subaddr;
337         i2c_mode = I2C_MASTER_WRITE;
338         i2c_ndata = ndata;
339         i2c_data = data;
340         i2c_seq = 0;
341         I2C_START();
342 }
343
344 void i2c_read(unsigned char addr, unsigned char subaddr, unsigned char *buf, int size)
345 {
346         i2c_addr = addr & 0xfe;
347         i2c_subaddr = subaddr;
348         i2c_mode = I2C_MASTER_READ;
349         i2c_ndata = size;
350         i2c_data = buf;
351         i2c_seq = 0;
352         I2C_START();
353 }
354
355 void i2c_handle_send(void)
356 {
357         unsigned char state = TWSR & 0xf8;
358
359         //printf("DBG i2c state (%x)\n", (unsigned int)state);
360         LOGNUM(state);
361
362         switch(state) {
363         case ST_REP_START:      /* 0x10 */
364                 /* repeated start, same as start, but also increment i2c_seq */
365                 i2c_seq++;
366         case ST_START:  /* 0x8 */
367                 /* start initiated, write the slave address */
368                 //printf("DBG i2c SLA: %x\n", (unsigned int)i2c_addr);
369                 TWDR = i2c_addr;
370                 I2C_WRITE();
371                 break;
372
373         case ST_SLA_W_ACK:      /* 0x18 */
374                 /* slave addr sent and ACKed, send subaddr or data */
375                 if(i2c_seq == 0) {
376                         /* this is the first packet, send subaddr */
377                         i2c_seq++;
378                         TWDR = i2c_subaddr;
379                         I2C_WRITE();
380                 } else {
381                         if(i2c_ndata--) {
382                                 TWDR = *i2c_data++;
383                                 I2C_WRITE();
384                         } else {
385                                 /* done sending, send stop */
386                                 i2c_mode = I2C_IDLE;
387                                 I2C_STOP();
388                         }
389                 }
390                 break;
391
392         case ST_SLA_W_NACK:     /* 0x20 */
393                 /* slave addr sent but not ACKed, abort */
394                 i2c_mode = I2C_IDLE;
395                 I2C_STOP();
396                 printf("i2c: NACK after SLA+W\n");
397                 break;
398
399         case ST_WR_ACK: /* 0x28 */
400                 /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
401 #if 0
402                 if(i2c_seq == 0) {
403                         /* subaddr was sent, send repeated start */
404                         I2C_START();
405                 } else {
406 #endif
407                         /* data was sent, send more data or stop */
408                         if(i2c_ndata--) {
409                                 TWDR = *i2c_data++;
410                                 I2C_WRITE();
411                         } else {
412                                 i2c_mode = I2C_IDLE;
413                                 I2C_STOP();
414                         }
415 //              }
416                 break;
417
418         case ST_WR_NACK:        /* 0x30 */
419                 /* data (or subaddr) sent but not ACKed */
420                 if(i2c_seq == 0) {
421                         /* NACK after subaddr, abort */
422                         printf("i2c: NACK after subaddr\n");
423                 } else {
424                         /* NACK after data */
425                         if(i2c_ndata) {
426                                 printf("i2c: NACK with %d pending\n", i2c_ndata);
427                         }
428                 }
429                 i2c_mode = I2C_IDLE;
430                 I2C_STOP();
431                 break;
432
433         case ST_ARBLOST:        /* 0x38 */
434                 /* arbitration lost */
435                 printf("i2c: arb lost\n");
436                 I2C_START();
437                 break;
438
439         case ST_INVALID:        /* 0 */
440                 printf("i2c: invalid start/stop\n");
441                 i2c_mode = I2C_IDLE;
442                 I2C_STOP();
443                 break;
444
445         default:
446                 printf("i2c: unexpected state (W): 0x%x\n", state);
447                 i2c_mode = I2C_IDLE;
448                 I2C_STOP();
449         }
450 }
451
452 void i2c_handle_recv(void)
453 {
454         unsigned char state = TWSR & 0xf8;
455
456         LOGNUM(state);
457
458         switch(state) {
459         case ST_START:          /* 0x8 */
460                 TWDR = i2c_addr;        /* start a *write* (we need to send the subaddress before reading) */
461                 I2C_WRITE();
462                 break;
463
464         case ST_REP_START:      /* 0x10 */
465                 /* repeated start, now we can issue the actual read */
466                 TWDR = i2c_addr | 1;
467                 I2C_WRITE();
468                 break;
469
470         case ST_SLA_W_ACK:      /* 0x18 */
471                 /* SLA+W means we just started the write part, need to send the subaddress */
472                 TWDR = i2c_subaddr;
473                 I2C_WRITE();
474                 break;
475
476         case ST_SLA_W_NACK:
477                 /* slave addr sent but not ACKed, abort */
478                 i2c_mode = I2C_IDLE;
479                 I2C_STOP();
480                 printf("i2c: NACK after SLA+W (R)\n");
481                 break;
482
483         case ST_WR_ACK:         /* 0x28 */
484                 /* the subaddress write was ACKed, rep-start to issue the read */
485                 I2C_START();
486                 break;
487
488         case ST_WR_NACK:
489                 /* the subaddress write was not ACKed, abort */
490                 i2c_mode = I2C_IDLE;
491                 I2C_STOP();
492                 printf("i2c: NACK after subaddr (R)\n");
493                 break;
494
495         case ST_SLA_R_ACK:      /* 0x40 */
496                 /* SLA+R was ACKed send ACK to start receiving */
497                 I2C_READ_ACK();
498                 break;
499
500         case ST_RD_ACK:         /* 0x50 */
501                 /* ... or last read was ACKed, again read next and ACK for more */
502                 *i2c_data++ = TWDR;
503                 if(--i2c_ndata > 0) {
504                         I2C_READ_ACK();
505                 } else {
506                         I2C_READ_NACK();
507                 }
508                 break;
509
510         case ST_SLA_R_NACK:     /* 0x48 */
511                 /* SLA+R was sent but ACK was not received, abort */
512                 i2c_mode = I2C_IDLE;
513                 I2C_STOP();
514                 printf("i2c: NACK after SLA+R\n");
515                 break;
516
517         case ST_RD_NACK:        /* 0x58 */
518                 /* read without ACK, we get this after we send a NACK, or this is the last byte (?) */
519                 if(i2c_ndata > 0) {
520                         *i2c_data++ = TWDR;
521                         i2c_ndata--;
522                 }
523                 i2c_mode = I2C_IDLE;
524                 I2C_STOP();
525                 break;
526
527         case ST_ARBLOST:        /* 0x38 */
528                 /* arbitration lost */
529                 printf("i2c: arb lost\n");
530                 I2C_START();
531                 break;
532
533         case ST_INVALID:        /* 0 */
534                 printf("i2c: invalid start/stop\n");
535                 i2c_mode = I2C_IDLE;
536                 I2C_STOP();
537                 break;
538
539         default:
540                 printf("i2c: unexpected state (R): 0x%x\n", state);
541                 i2c_mode = I2C_IDLE;
542                 I2C_STOP();
543         }
544 }
545
546 ISR(TWI_vect)
547 {
548         switch(i2c_mode) {
549         case I2C_MASTER_WRITE:
550                 i2c_handle_send();
551                 break;
552
553         case I2C_MASTER_READ:
554                 i2c_handle_recv();
555                 break;
556         }
557 }
558
559 void i2c_wait(void)
560 {
561         while(i2c_mode);
562 }
563
564 void i2c_async(void (*donecb)(void))
565 {
566         async_func = donecb;
567 }
568
569 void i2c_check_async(void)
570 {
571         if(async_func && i2c_mode == I2C_IDLE) {
572                 async_func();
573                 async_func = 0;
574         }
575 }
576
577 void i2c_hold(void)
578 {
579         TWCR = 0;               /* make sure the AVR i2c hardware is disabled */
580         DDRC = 0x20;    /* ... and drive SCL pin low */
581         PORTC = 0;
582 }
583
584 void i2c_release(void)
585 {
586         DDRC = 0;
587 }
588
589 #define PC_SCL  5
590 #define PC_SDA  4
591 #define PC_SCL_BIT      (1 << PC_SCL)
592 #define PC_SDA_BIT      (1 << PC_SDA)
593
594 static unsigned char pcprev, state;
595 static unsigned char value;
596 static int nbits;
597
598 /* use PCINT12,13 to snoop i2c traffic */
599 void i2c_snoop(int onoff)
600 {
601         snooping = onoff;
602
603         if(!onoff) {
604 #ifdef USE_PCINT
605                 /* to stop snooping just disable the interrupt */
606                 cli();
607                 PCMSK1 = 0;
608                 PCICR &= ~(1 << PCIE1);
609                 sei();
610 #endif
611                 return;
612         }
613
614         TWCR = 0;               /* make sure the i2c hw is disabled */
615         DDRC = 0;
616         PORTC = 0;
617
618 #ifdef USE_PCINT
619         cli();
620         PCICR |= 1 << PCIE1;    /* enable the pin-change interrupt */
621         PCMSK1 = (1 << PCINT12) | (1 << PCINT13);
622 #endif
623
624         pcprev = PINC;
625         state = 0;
626         value = 0;
627         nbits = 0;
628 #ifdef USE_PCINT
629         sei();
630 #endif
631 }
632
633 #ifdef USE_PCINT
634 ISR(PCINT1_vect)
635 #else
636 void snoop(void)
637 #endif
638 {
639         unsigned char pinc, delta;
640
641         pinc = PINC;
642         delta = pinc ^ pcprev;
643         pcprev = pinc;
644
645         if(delta & PC_SDA_BIT) {
646                 if(pinc & PC_SCL_BIT) {
647                         /* clock is high, so this is either a start or a stop */
648                         if(pinc & PC_SDA_BIT) {
649                                 EVLOG(EV_STOP, 0);
650                                 state = 0;
651                         } else {
652                                 EVLOG(EV_START, 0);
653                                 state = EV_START;
654                                 value = 0;
655                                 nbits = 0;
656                         }
657                         return;
658                 }
659         }
660
661         if(!state) {
662                 uint16_t val, ev;
663                 if(evq_wr != evq_rd) {
664                         val = evq[evq_rd];
665                         evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
666                         ev = val >> 8;
667                         if(ev <= EV_STOP) {
668                                 printf("%s\n", evname[ev]);
669                         } else {
670                                 printf("%s: %02x\n", evname[ev], val & 0xff);
671                         }
672                 }
673                 return;
674         }
675
676         if(delta & PC_SCL_BIT) {
677                 if(pinc & PC_SCL_BIT) {
678                         /* clock is going high, shift SDA */
679                         value = (value << 1) | ((pinc >> PC_SDA) & 1);
680                         if(++nbits >= 8) {
681                                 switch(state) {
682                                 case EV_START:
683                                         state = (value & 1) ? EV_SLA_R : EV_SLA_W;
684                                         EVLOG(state, value & 0xfe);
685                                         break;
686                                 case EV_SLA_W:
687                                 case EV_SLA_R:
688                                         state = EV_DATA;
689                                         EVLOG(state, value);
690                                 case EV_DATA:
691                                         //EVLOG(state, value);
692                                 default:
693                                         break;
694                                 }
695                                 nbits = 0;
696                                 value = 0;
697                         }
698                 }
699         }
700
701         PORTB ^= 1;
702 }