The iPod controller that I submitted for the mbed challenge

Dependencies:   mbed Motordriver PID

Revision:
0:371773dd3dd1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ipod.cpp	Wed May 04 15:41:13 2011 +0000
@@ -0,0 +1,380 @@
+#include "mbed.h"
+#include "ipod.h"
+
+ipod::ipod(PinName tx, PinName rx): led(*new DigitalOut(DEBUGPIN)) {
+    com = new Serial(tx, rx);
+    com->baud(19200);//should work upto 57600
+    rx_ready = false;
+    tx_ready = true;
+    state = 0;
+    tx_buffer[0] = 0xff;
+    tx_buffer[1] = 0x55;
+    com->attach(this, &ipod::handlerx);
+#ifdef TX_IRQ
+    com->attach(this, &ipod::handletx, Serial::TxIrq);
+#endif
+    lastcommand = -1;
+    error = err_success;
+    replies = 0;
+}
+
+void ipod::handlerx() {//Serial port interrupt handler
+    led = 1;
+    unsigned short c = com->getc();
+//   printf("%d: %02X; ", state, chk);
+    switch (state) {
+        case 0:
+            if (c==0xff) state++;
+            break;
+        case 1:
+            if (c==0x55) state++;
+            else state = 0;
+            break;
+        case 2:
+            if (rx_ready) {
+                state = 0;
+                break;//buffer not free, ignore the message
+            }
+            length = c;
+            chk = c;
+            rx_buffer = new char[length];
+            state++;
+            break;
+        case 3:
+            if (c > 4) {
+                state = 0;
+                break;//mode not known or not supported
+            }
+            mode = c;
+            chk += c;
+            state++;
+            break;
+        case 4:
+            command =  c<<8;
+            chk += c;
+            state++;
+            break;
+        case 5:
+            command += c;
+            chk += c;
+            state++;
+            break;
+        default:
+            chk += c;
+            if (state < length+3) {
+                rx_buffer[state-6] = c;
+                state++;
+            } else {
+                if (chk==0) {
+                    rx_ready = true;
+                    replies--;
+                    if (command-1 == lastcommand) {//it is a reply to lastcommand
+                        //printf("reply to %02X\n", lastcommand);
+                        lastcommand = -1;
+                    }
+                    //printf("ready %02X\n", command);
+                } else
+                    printf("checksum error %02x state=%d\n", chk, state);
+                state = 0;
+                break;
+            }
+    }
+    led = 0;
+}
+
+void ipod::handletx() {
+    if (tx_index < tx_size)
+        com->putc(tx_buffer[tx_index++]);
+    else
+        tx_ready = true;
+}
+
+bool ipod::waitForReply() {//busy waits for a reply to the last issued command, all other replies are ignored
+    for (int i = 0; i < 10000000; i++) {
+        if (ready()) {
+            if (lastcommand == -1) {//indicates that a reply to the last issued command has been received
+                parse();//parse the last reply
+                return true;//return to caller for further processing
+            }
+            release();//release the buffer and hence ignore the message
+        }
+    }
+    printf("timeout: last received %02X(%d, %d, %d, \"%s\")\n", command, arg1, arg2, arg3, string);
+    error = err_timeout;
+    return false;
+}
+
+#define BIGENDIAN
+void ipod::copy(char *b, union conv p) {//copy an UINT32 argument bytewise to the buffer
+#ifdef BIGENDIAN
+    for (int i = 0; i < 4; i++)
+        *(b+3-i) = p.asBytes[i];
+#else
+    for (int i = 0; i < 4; i++)
+        *(b+i) = p.asBytes[i];
+#endif
+}
+
+void ipod::SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//send an advanced ipod remote command, unused arguments are optional
+    union conv par1, par2, par3;
+    par1.asInt = arg1;
+    par2.asInt = arg2;
+    par3.asInt = arg3;
+    tx_buffer[3] = 4; //AiR mode
+    tx_buffer[4] = 0;
+    tx_buffer[5] = cmd & 0xff;
+    unsigned expect = 1; //typically expect 1 reply per request
+    switch (cmd) {
+        case 0x12: //get ipod type
+            tx_buffer[2] = 3;
+            //expect 2 bytes return
+            break;
+        case 0x14: //get iPod name
+            tx_buffer[2] = 3;
+            //expect string return
+            break;
+        case 0x16: //main lib
+            tx_buffer[2] = 3;
+            expect = 0;
+            break;
+        case 0x17: //goto item
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 8;
+            copy(tx_buffer+7, par2);
+            expect = 0;
+            break;
+        case 0x18: //get count
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 4;
+            //expect integer return
+            break;
+        case 0x1A: //get names
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 12;
+            copy(tx_buffer+7, par2);
+            copy(tx_buffer+11, par3);
+            //expect many offset,string pairs
+            expect = arg3;
+            break;
+        case 0x1C: //get time/stat
+            tx_buffer[2] = 3;
+            //expect int,int,byte
+            break;
+        case 0x1E: //get position
+            tx_buffer[2] = 3;
+            //expect int
+            break;
+        case 0x20: //get title
+            tx_buffer[2] = 7;
+            copy(tx_buffer+6, par1);
+            //expect string
+            break;
+        case 0x22: //get artist
+            tx_buffer[2] = 7;
+            copy(tx_buffer+6, par1);
+            //expect string
+            break;
+        case 0x24: //get album
+            tx_buffer[2] = 7;
+            copy(tx_buffer+6, par1);
+            //expect string
+            break;
+        case 0x26: //poll mode
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 4;
+            //expect many byte,int pairs
+            expect = 0; //the number to expect is unknown here, so either stop polling or just insert command anyway
+            break;
+        case 0x28: //run PL
+            tx_buffer[2] = 7;
+            copy(tx_buffer+6, par1);
+            expect = 0;
+            break;
+        case 0x29: //command
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 4;
+            expect = 0;
+            break;
+        case 0x2C: //get shuffle
+            tx_buffer[2] = 3;
+            //expect byte
+            break;
+        case 0x2E: //set shuffle
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 4;
+            expect = 0;
+            break;
+        case 0x2F: //get repeat
+            tx_buffer[2] = 3;
+            //expect byte
+            break;
+        case 0x31: //set repeat
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 4;
+            expect = 0;
+            break;
+        case 0x35: //get nr in PL
+            tx_buffer[2] = 3;
+            //expect int
+            break;
+        case 0x37: //goto song
+            tx_buffer[2] = 7;
+            copy(tx_buffer+6, par1);
+            expect = 0;
+            break;
+        case 0x38: //select
+            tx_buffer[6] = arg1;
+            tx_buffer[2] = 9;
+            copy(tx_buffer+7, par2);
+            tx_buffer[11] = 0;//unknown
+            break;
+        default:
+            return;
+    }
+    tx_size = tx_buffer[2] + 4;
+    tx_buffer[tx_size-1] = 0;
+    for (int i = 2; i < tx_size-1; i++)
+        tx_buffer[tx_size-1] -= tx_buffer[i];//compute checksum
+    tx_index = 1;
+    replies = expect;
+#ifdef TX_IRQ
+    while (!tx_ready) /* wait */;
+    tx_ready = false;
+    com->putc(tx_buffer[0]);//kick-off writing buffer
+#else
+    write();
+#endif
+    lastcommand = cmd;
+    printf("%02X (%d, %d, %d)\n", cmd, arg1, arg2, arg3);
+}
+
+void ipod::guarded_SendAirCmd(unsigned cmd, unsigned arg1, unsigned arg2, unsigned arg3) {//same as SendAirCmd but waits until all previous expected replies have been received
+    for (int i = 0; i<1000000; i++)
+        if (replies==0) {
+            SendAirCmd(cmd, arg1, arg2, arg3);
+            return;
+        }
+    printf("Timeout while waiting for reply to %02X, command %02X cannot be send\n", lastcommand, cmd);
+}
+
+void ipod::SetMode(int m) { //char buf[] = {0xff, 0x55, 0x03, 0x00, 0x01, 0x00, 0x00};
+    tx_buffer[2] = 3; //length
+    tx_buffer[3] = 0; //mode 0, mode switching
+    tx_buffer[4] = 1; //cmd high byte
+    tx_buffer[5] = m; //cmd low byte
+    tx_buffer[6] = 0x100 - 3 - 1 - m;//checksum
+    tx_index = 1;
+    tx_size = 7;
+#ifdef TX_IRQ
+    while (!tx_ready) /* wait */;
+    tx_ready = false;
+    com->putc(tx_buffer[0]);
+#else
+    write();
+#endif
+}
+
+unsigned ipod::copy(char* s) {//return a bytewise argument from the buffer as a UINT32
+    union conv p;
+#ifdef BIGENDIAN
+    for (int i = 0; i < 4; i++)
+        p.asBytes[3-i] = s[i];
+#else
+    for (int i = 0; i < 4; i++)
+        p.asBytes[i] = s[i];
+#endif
+    return p.asInt;
+}
+
+void ipod::parse() {
+    error = err_success;
+    switch (mode) {
+        case 0: //mode 0 reply
+            if (command & 0xFF == 4) {//reply to mode status request
+                printf("mode 0 reply = %04X\n", command);
+            } else
+                printf( "unexpected mode 0 reply\n");
+            break;  //assume s.c_str()[4] == 0
+        case 4://reply to AiR command
+            switch (command) {
+                case 0:
+                    if (rx_buffer[0]==0x04) {
+                        error = err_unknown;
+                        printf("command %04X is not understood\n", *(unsigned short*)&rx_buffer[1]);
+                    }
+                    break;
+                case 1:
+                    error = (errors)rx_buffer[0];
+                    switch (error) {
+                        case err_success:
+                            //printf("command %04X succeeded\n",*(unsigned short*)&rx_buffer[1]);
+                            break;
+                        case err_failure:
+                            printf("command %04X failed\n",*(unsigned short*)&rx_buffer[1]);
+                            break;
+                        case err_limit:
+                            printf("command %04X had wrong parameter(s)\n",*(unsigned short*)&rx_buffer[1]);
+                            break;
+                        case err_answer:
+                            printf("command %04X is an answer\n",*(unsigned short*)&rx_buffer[1]);
+                            break;
+                        default:
+                            printf("unknown error\n");
+                            break;
+                    }
+                    break;
+                    //2 bytes
+                case get_ipod_size+1: //ipod type
+                    break;
+                    //single string
+                case get_ipod_name+1:
+                case get_title+1:
+                case get_artist+1:
+                case get_album+1:
+                    string = rx_buffer;
+                    printf("%04X: %s\n", command, string);
+                    break;
+                    //number+string
+                case get_names+1:
+                    string = rx_buffer + 4;
+                    arg1 = copy(rx_buffer);
+                    printf("%04X: %d %s\n", command, arg1, string);
+                    break;
+                    //number+number+byte
+                case get_time_status+1:
+                    arg1 = copy(rx_buffer);
+                    arg2 = copy(rx_buffer+4);
+                    arg3 = rx_buffer[8];
+                    printf("%04X: %d %d %02X\n", command, arg1, arg2, arg3);
+                    break;
+                    //number
+                case get_count+1:
+                case get_position+1:
+                case get_nr_in_playlist+1:
+                    arg1 = copy(rx_buffer);
+                    printf("%04X: %d\n", command, arg1);
+                    break;
+                    //byte + number
+                case polling+1:
+                    arg1 = rx_buffer[0];
+                    arg2 = copy(rx_buffer+1);
+                    break;
+                    //byte
+                case get_shuffle+1:
+                case get_repeat+1:
+                    arg1 = rx_buffer[0];
+                    printf("%04X: %02X\n", command, arg1);
+                    break;
+                    //10 bytes
+                case select+1: //select
+                    break;
+                default:
+                    printf("unsupported reply");
+                    break;
+            }
+            break;
+        default:
+            printf("unsupported mode\n");
+            break;
+    }
+}
\ No newline at end of file