diff --git a/apps/iap.c b/apps/iap.c index eaee21b..efb3ebc 100644 --- a/apps/iap.c +++ b/apps/iap.c @@ -21,6 +21,7 @@ #include #include +#include "panic.h" #include "iap.h" #include "button.h" #include "config.h" @@ -46,6 +47,16 @@ #include "filetree.h" #include "dir.h" +#define LOGF_ENABLE +#include "logf.h" + +#define MS_PER_HZ (1000/HZ) +/* IAP specifies a timeout of 25ms for traffic from a device to the iPod. + * Depending on HZ this cannot be accurately measured. Find out the next + * best thing. + */ +#define IAP_PKT_TIMEOUT ((25+MS_PER_HZ-1)/MS_PER_HZ) + static volatile int iap_pollspeed = 0; static volatile bool iap_remotetick = true; static bool iap_setupflag = false, iap_updateflag = false; @@ -56,13 +67,74 @@ static int iap_repeatbtn = 0; static bool iap_btnrepeat = false, iap_btnshuffle = false; static unsigned char serbuf[RX_BUFLEN]; -static int serbuf_i = 0; +static volatile bool serbuf_lock = false; static unsigned char response[TX_BUFLEN]; -static int responselen; static char cur_dbrecord[5] = {0}; +/* states of the iap de-framing state machine */ +enum fsm_state { + ST_SYNC, /* wait for 0xFF sync byte */ + ST_SOF, /* wait for 0x55 start-of-frame byte */ + ST_LEN, /* receive length byte (small packet) */ + ST_LENH, /* receive length high byte (large packet) */ + ST_LENL, /* receive length low byte (large packet) */ + ST_DATA, /* receive data */ + ST_CHECK /* verify checksum */ +}; + +static struct state_t { + enum fsm_state state; /* current fsm state */ + unsigned int len; /* payload data length */ + unsigned char *payload; /* payload data pointer */ + unsigned int check; /* running checksum over [len,payload,check] */ + unsigned int count; /* playload bytes counter */ +} frame_state = { + .state = ST_SYNC +}; + +/* Convert a buffer into a printable string, perl style + * buf contains the data to be converted, len is the length + * of the buffer. + * + * This will convert at most 1024 bytes from buf + */ +static char* hexstring(unsigned char *buf, unsigned int len) { + static char hexbuf[4097]; + unsigned int l; + unsigned char* p; + unsigned char* out; + unsigned char h[] = {'0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + if (len > 1024) { + l = 1024; + } else { + l = len; + } + + p = buf; + out = hexbuf; + do { + if (*p == 92) { + *out++ = '\\'; + *out++ = '\\'; + } else if ((*p>= 0x20) && (*p < 0x7F)) { + *out++ = *p; + } else { + *out++ = '\\'; + *out++ = 'x'; + *out++ = h[(*p)>>4]; + *out++ = h[*p & 0x0F]; + } + } while(--l && p++); + + *out = 0x00; + + return hexbuf; +} + static void put_u32(unsigned char *buf, uint32_t data) { buf[0] = (data >> 24) & 0xFF; @@ -142,10 +214,12 @@ void iap_bitrate_set(int ratenum) void iap_send_pkt(const unsigned char * data, int len) { - int i, chksum; + int i, chksum, responselen; if(len > TX_BUFLEN-4) len = TX_BUFLEN-4; responselen = len + 4; + + logf("T: %s", hexstring(data, len)); response[0] = 0xFF; response[1] = 0x55; @@ -168,37 +242,97 @@ void iap_send_pkt(const unsigned char * data, int len) bool iap_getc(unsigned char x) { - static unsigned char last_x = 0; - static bool newpkt = true; - static unsigned char chksum = 0; - - /* Restart if the sync word is seen */ - if(x == 0x55 && last_x == 0xff/* && newpkt*/) - { - serbuf[0] = 0; - serbuf_i = 0; - chksum = 0; - newpkt = false; - } - else - { - if(serbuf_i >= RX_BUFLEN) - serbuf_i = 0; - - serbuf[serbuf_i++] = x; - chksum += x; + struct state_t *s = &frame_state; + static long pkt_timeout; + + /* Check the time since the last packet arrived. */ + if ((s->state != ST_SYNC) && TIME_AFTER(current_tick, pkt_timeout)) { + /* Packet timeouts only make sense while not waiting for the + * sync byte */ + serbuf_lock = false; + s->state = ST_SYNC; + return iap_getc(x); } - last_x = x; - /* Broadcast to queue if we have a complete message */ - if(serbuf_i && (serbuf_i == serbuf[0]+2)) - { - serbuf_i = 0; - newpkt = true; - if(chksum == 0) + + /* run state machine to detect and extract a valid frame */ + switch (s->state) { + case ST_SYNC: + if (x == 0xFF) { + s->state = ST_SOF; + } + break; + case ST_SOF: + if (x == 0x55) { + /* received a valid sync/SOF pair */ + s->state = ST_LEN; + } else { + s->state = ST_SYNC; + return iap_getc(x); + } + break; + case ST_LEN: + /* try to get a lock on serbuf */ + if (serbuf_lock) { + s->state = ST_SYNC; + break; + } + serbuf_lock = true; + + s->check = x; + s->count = 0; + s->payload = serbuf; + if (x == 0) { + /* large packet */ + s->state = ST_LENH; + } else { + /* small packet */ + s->len = x; + s->state = ST_DATA; + } + break; + case ST_LENH: + s->check += x; + s->len = x << 8; + s->state = ST_LENL; + break; + case ST_LENL: + s->check += x; + s->len += x; + if ((s->len == 0) || (s->len > RX_BUFLEN)) { + /* invalid length */ + serbuf_lock = false; + s->state = ST_SYNC; + return iap_getc(x); + } else { + s->state = ST_DATA; + } + break; + case ST_DATA: + s->check += x; + s->payload[s->count++] = x; + if (s->count == s->len) { + s->state = ST_CHECK; + } + break; + case ST_CHECK: + s->check += x; + if ((s->check & 0xFF) == 0) { + /* done, received a valid frame */ + logf("R: %s", hexstring(s->payload, s->len)); queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0); + } + s->state = ST_SYNC; + break; + default: + panicf("Unhandled iap state %d", (int) s->state); + break; } - return newpkt; + + pkt_timeout = current_tick + IAP_PKT_TIMEOUT; + + /* return true while still hunting for the sync and start-of-frame byte */ + return (s->state == ST_SYNC) || (s->state == ST_SOF); } void iap_periodic(void) @@ -1004,8 +1138,9 @@ static void iap_handlepkt_mode7(unsigned int len, const unsigned char *buf) void iap_handlepkt(void) { + struct state_t *s = &frame_state; + if(!iap_setupflag) return; - if(serbuf[0] == 0) return; /* if we are waiting for a remote button to go out, delay the handling of the new packet */ @@ -1015,20 +1150,18 @@ void iap_handlepkt(void) return; } - /* get length and payload from serbuf */ - unsigned int len = serbuf[0]; - unsigned char *payload = &serbuf[1]; - - unsigned char mode = payload[0]; + /* handle command by mode */ + unsigned char mode = s->payload[0]; switch (mode) { - case 0: iap_handlepkt_mode0(len, payload); break; - case 2: iap_handlepkt_mode2(len, payload); break; - case 3: iap_handlepkt_mode3(len, payload); break; - case 4: iap_handlepkt_mode4(len, payload); break; - case 7: iap_handlepkt_mode7(len, payload); break; + case 0: iap_handlepkt_mode0(s->len, s->payload); break; + case 2: iap_handlepkt_mode2(s->len, s->payload); break; + case 3: iap_handlepkt_mode3(s->len, s->payload); break; + case 4: iap_handlepkt_mode4(s->len, s->payload); break; + case 7: iap_handlepkt_mode7(s->len, s->payload); break; } - - serbuf[0] = 0; + + /* release the lock on serbuf */ + serbuf_lock = false; } int remote_control_rx(void) diff --git a/firmware/export/iap.h b/firmware/export/iap.h index c9a670a..8ee26cb 100644 --- a/firmware/export/iap.h +++ b/firmware/export/iap.h @@ -22,7 +22,7 @@ #include -#define RX_BUFLEN 260 +#define RX_BUFLEN 512 #define TX_BUFLEN 128 extern bool iap_getc(unsigned char x);