X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?p=tv_i2c_hack;a=blobdiff_plain;f=main.c;fp=main.c;h=0000000000000000000000000000000000000000;hp=fa0be2f79e51077a8cb1f3a4a22668183acff1e0;hb=3b128b470c001555ba1c69ba8b0c96994182f8a1;hpb=efb5b9c4333f6034595b30d7d549fab2b120d9c9 diff --git a/main.c b/main.c deleted file mode 100644 index fa0be2f..0000000 --- a/main.c +++ /dev/null @@ -1,702 +0,0 @@ -#include -#include -#include -#include -#include -#include -#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; -}