+ 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();
+ }