+++ /dev/null
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <avr/io.h>
-#include <avr/interrupt.h>
-#include <util/delay.h>
-#include "serial.h"
-
-#undef USE_PCINT
-
-enum {
- I2C_IDLE,
- 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)
-#define TWIE_BIT (1 << TWIE)
-
-#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;
-int i2c_seq, i2c_ndata;
-unsigned char *i2c_data;
-int i2c_retry_delay;
-
-#define EVQ_SIZE 64
-uint16_t evq[EVQ_SIZE];
-volatile int evq_wr, evq_rd;
-
-#define LOGNUM(x) EVLOG(EV_DEBUG, (x))
-
-#define EVLOG(ev, data) \
- do { \
- 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)
-
-/* serial input buffer */
-static char input[64];
-static unsigned char inp_cidx;
-
-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);
-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 = 0; /* I2C pins as inputs */
- DDRD = 0;
- PORTB = 0;
- PORTC = 0;
- PORTD = 0;
-
- TWBR = 10; /* 10 with 1x prescaler should make it about 100khz */
- TWSR = 0; /* prescaler 1x */
- TWAR = 0;
- TWAMR = 0;
- TWCR = 0; /* I2C disabled by default */
-
- i2c_mode = I2C_IDLE;
- i2c_snoop(0); /* disable snooping by default */
-
- init_serial(38400);
- sei();
-
- printf("Starting i2c hack\n");
-
- for(;;) {
-#ifndef USE_PCINT
- if(snooping) {
- snoop();
- if(have_input()) {
- i2c_snoop(0);
- }
- continue;
- }
-#endif
-
- i2c_check_async();
-
- if(have_input()) {
- int c = getchar();
- putchar(c);
-
- if(c == '\r' || c == '\n') {
- putchar('\n');
- input[inp_cidx] = 0;
- proc_cmd(input);
- inp_cidx = 0;
- } else if(inp_cidx < sizeof input - 1) {
- input[inp_cidx++] = c;
- }
- }
-
- /* read from queue and send over the serial port */
- val = 0xffff;
- cli();
- if(evq_wr != evq_rd) {
- val = evq[evq_rd];
- evq_rd = (evq_rd + 1) & (EVQ_SIZE - 1);
- }
- sei();
-
- if(val != 0xffff) {
- 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);
- }
- }
- }
- return 0;
-}
-
-static void proc_cmd(char *input)
-{
- char *endp;
-
- if(strcmp(input, "dbg") == 0) {
- printf("OK dbg\n");
- dbgmode = 1;
-
- } 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] = 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 vol (%s)\n", input + 4);
- } else {
- data[0] = vol;
- i2c_write(0x8a, 0x1f, data, 1);
- printf("OK volume: %d\n", vol);
- }
-
- } else if(memcmp(input, "sat", 3) == 0) {
- printf("OK sat\n");
- data[0] = atoi(input + 3);
- if(data[0] <= 0 || data[0] > 0x3f) {
- data[0] = 0x1f;
- }
- 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\n");
- } else {
- printf("ERR i2c is idle\n");
- }
-
- } else {
- printf("ERR command (%s)\n", input);
- }
-}
-
-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_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;
-
- //printf("DBG i2c state (%x)\n", (unsigned int)state);
- LOGNUM(state);
-
- switch(state) {
- case ST_REP_START: /* 0x10 */
- /* repeated start, same as start, but also increment i2c_seq */
- i2c_seq++;
- 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 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++;
- TWDR = i2c_subaddr;
- I2C_WRITE();
- } else {
- if(i2c_ndata--) {
- TWDR = *i2c_data++;
- I2C_WRITE();
- } else {
- /* done sending, send stop */
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
- }
- break;
-
- 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 ST_WR_ACK: /* 0x28 */
- /* data (or subaddr) sent and ACKed, send more data (or restart) if available */
-#if 0
- if(i2c_seq == 0) {
- /* subaddr was sent, send repeated start */
- I2C_START();
- } else {
-#endif
- /* data was sent, send more data or stop */
- if(i2c_ndata--) {
- TWDR = *i2c_data++;
- I2C_WRITE();
- } else {
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
-// }
- break;
-
- case ST_WR_NACK: /* 0x30 */
- /* data (or subaddr) sent but not ACKed */
- if(i2c_seq == 0) {
- /* NACK after subaddr, abort */
- printf("i2c: NACK after subaddr\n");
- } else {
- /* NACK after data */
- if(i2c_ndata) {
- printf("i2c: NACK with %d pending\n", 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 (W): 0x%x\n", state);
- i2c_mode = I2C_IDLE;
- I2C_STOP();
- }
-}
-
-void i2c_handle_recv(void)
-{
- 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)
-{
- switch(i2c_mode) {
- case I2C_MASTER_WRITE:
- i2c_handle_send();
- break;
-
- case I2C_MASTER_READ:
- i2c_handle_recv();
- 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)
-
-static unsigned char pcprev, state;
-static unsigned char value;
-static int nbits;
-
-/* 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;
- }
-
- 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;
-}