/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by Alan Korr & Nick Robinson
 *
 * All files in this archive are subject to the GNU General Public License.
 * See the file COPYING in the source tree root for full license agreement.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <memory.h>
#include <string.h>

#include "iap.h"
#include "button.h"
#include "config.h"
#include "cpu.h"
#include "system.h"
#include "kernel.h"
#include "serial.h"
#include "appevents.h"

#include "playlist.h"
#include "playback.h"
#include "audio.h"
#include "settings.h"
#include "metadata.h"
#include "wps.h"
#include "sound.h"
#include "action.h"
#include "powermgmt.h"

#include "tuner.h"
#include "ipod_remote_tuner.h"

static volatile int iap_pollspeed = 0;
static volatile bool iap_remotetick = true;
static bool iap_setupflag = false, iap_updateflag = false;
static int iap_changedctr = 0;

static unsigned long iap_remotebtn = 0;
static int iap_repeatbtn = 0;
static bool iap_btnrepeat = false, iap_btnshuffle = false;

unsigned char serbuf[RX_BUFLEN];
static int serbuf_i = 0;

static unsigned char response[TX_BUFLEN];
static int responselen;

static void iap_task(void)
{
    static int count = 0;

    count += iap_pollspeed;
    if (count < (500/10)) return;

    /* exec every 500ms if pollspeed == 1 */
    count = 0;
    queue_post(&button_queue, SYS_IAP_PERIODIC, 0);
}

/* called by playback when the next track starts */
static void iap_track_changed(void *ignored)
{
    (void)ignored;
    iap_changedctr = 1;
}

void iap_setup(int ratenum)
{
    iap_bitrate_set(ratenum);
    iap_pollspeed = 0;
    iap_remotetick = true;
    iap_updateflag = false;
    iap_changedctr = 0;
    iap_setupflag = true;
    iap_remotebtn = BUTTON_NONE;
    tick_add_task(iap_task);
    add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, iap_track_changed);
}

void iap_bitrate_set(int ratenum)
{
    switch(ratenum)
    {
        case 0:
            serial_bitrate(0);
            break;
        case 1:
            serial_bitrate(9600);
            break;
        case 2:
            serial_bitrate(19200);
            break;
        case 3:
            serial_bitrate(38400);
            break;
        case 4:
            serial_bitrate(57600);
            break;
    }
}

/* Message format:
   0xff
   0x55
   length
   mode
   command (2 bytes)
   parameters (0-n bytes)
   checksum (length+mode+parameters+checksum == 0)
*/

void iap_send_pkt(const unsigned char * data, int len)
{
    int i, chksum;
    
    if(len > TX_BUFLEN-4) len = TX_BUFLEN-4;
    responselen = len + 4;
    
    response[0] = 0xFF;
    response[1] = 0x55;
    
    chksum = response[2] = len;
    for(i = 0; i < len; i ++)
    {
        chksum += data[i];
        response[i+3] = data[i];
    }

    response[i+3] = 0x100 - (chksum & 0xFF);
    
    for(i = 0; i < responselen; i ++)
    {
        while (!tx_rdy()) ;
        tx_writec(response[i]);
    }
}    

int 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;
    }
    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)
            queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
    }
    return newpkt;
}

void iap_periodic(void)
{
    if(!iap_setupflag) return;
    if(!iap_pollspeed) return;
    
    unsigned char data[] = {0x04, 0x00, 0x27, 0x04, 0x00, 0x00, 0x00, 0x00};
    unsigned long time_elapsed = audio_current_track()->elapsed;

    time_elapsed += wps_get_ff_rewind_count();

    data[3] = 0x04; /* playing */

    /* If info has changed, don't flag it right away */
    if(iap_changedctr && iap_changedctr++ >= iap_pollspeed * 2)
    {
        /* track info has changed */
        iap_changedctr = 0;
        data[3] = 0x01; /* 0x02 has same effect?  */
        iap_updateflag = true;
    }

    data[4] = time_elapsed >> 24;
    data[5] = time_elapsed >> 16;
    data[6] = time_elapsed >> 8;
    data[7] = time_elapsed;
    iap_send_pkt(data, sizeof(data));
}

static void iap_set_remote_volume(void)
{
    unsigned char data[] = {0x03, 0x0D, 0x04, 0x00, 0x00};
    data[4] = (char)((global_settings.volume+58) * 4);
    iap_send_pkt(data, sizeof(data));
}

void iap_handlepkt(void)
{

    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 */
    if(!iap_remotetick)
    {
        queue_post(&button_queue, SYS_IAP_HANDLEPKT, 0);
        return;
    }

    /* Handle Mode 0 */
    if (serbuf[1] == 0x00)
    {
        switch (serbuf[2])
        {
            case 0x24:
            {
                /* ipod video send this */
                unsigned char data[] = {0x00, 0x25, 0x00, 0x00, 0x00,
                                                0x00, 0x00, 0x00, 0x00,0x01};
                iap_send_pkt(data, sizeof(data));
                break;
            }

            case 0x18:
            {
                /* ciphered authentication command */
                /* Isn't used since we don't send the 0x00 0x17 command */
                break;
            }

            case 0x15:
            {
                unsigned char data0[] = {0x00, 0x16, 0x00};
                iap_send_pkt(data0, sizeof(data0));
                unsigned char data1[] = {0x00, 0x27, 0x00};
                iap_send_pkt(data1, sizeof(data1));
                /* authentication ack, mandatory to enable some hardware */
                unsigned char data2[] = {0x00, 0x19, 0x00};
                iap_send_pkt(data2, sizeof(data2));
                if (radio_present == 1)
                {
                    /* get tuner capacities */
                    unsigned char data3[] = {0x07, 0x01};
                    iap_send_pkt(data3, sizeof(data3));
                }
                iap_set_remote_volume();
                break;
            }

            case 0x13:
            {
                unsigned char data[] = {0x00, 0x02, 0x00, 0x13};
                iap_send_pkt(data, sizeof(data));

                if (serbuf[6] == 0x35)
                /* FM transmitter sends this: */
                /* FF 55 0E 00 13 00 00 00 35 00 00 00 04 00 00 00 00 A6 (??)*/
                {
                    unsigned char data2[] = {0x00, 0x27, 0x00};
                    iap_send_pkt(data2, sizeof(data2));
                    unsigned char data3[] = {0x05, 0x02};
                    iap_send_pkt(data3, sizeof(data3));
                }

                else
                {
                    /* ipod fm remote sends this: */ 
                    /* FF 55 0E 00 13 00 00 00 8D 00 00 00 0E 00 00 00 03 41 */
                    if (serbuf[6] |= 0x80)
                        radio_present = 1;
                    unsigned char data4[] = {0x00, 0x14};
                    iap_send_pkt(data4, sizeof(data4));
                }
                break;
            }

            /* Init */
            case 0x0F:
            {
                unsigned char data[] = {0x00, 0x10, 0x00, 0x01, 0x05};
                data[2] = serbuf[3];
                iap_send_pkt(data, sizeof(data));
                break;
            }

            /* get model info */
            case 0x0D:
            {
                /* ipod is supposed to work only with 5G and nano 2G */
                /*{0x00, 0x0E, 0x00, 0x0B, 0x00, 0x05, 0x50, 0x41, 0x31, 0x34, 
                        0x37, 0x4C, 0x4C, 0x00};    PA147LL (IPOD 5G 60 GO) */
                unsigned char data[] = {0x00, 0x0E, 0x00, 0x0B, 0x00, 0x10,
                                    'R', 'O', 'C', 'K', 'B', 'O', 'X', 0x00};
                iap_send_pkt(data, sizeof(data));
                break;
            }

            /* Ipod FM remote sends this: FF 55 02 00 09 F5  */
            case 0x09:
            {
                /* ipod5G firmware version */
                unsigned char data[] = {0x00, 0x0A, 0x01, 0x02, 0x01 };
                iap_send_pkt(data, sizeof(data));
                break;
            } 

            /* FM transmitter sends this: */
            /* FF 55 02 00 05 F9 (mode switch: AiR mode) */
            case 0x05:
            {
                unsigned char data[] = {0x00, 0x02, 0x06,
                                        0x05, 0x00, 0x00, 0x0B, 0xB8, 0x28};
                iap_send_pkt(data, sizeof(data));
                unsigned char data2[] = {0x00, 0x02, 0x00, 0x05};
                iap_send_pkt(data2, sizeof(data2));
                break;
            }

            case 0x01:
            {
                /* FM transmitter sends this: */
                /* FF 55 06 00 01 05 00 02 01 F1 (mode switch) */
                if(serbuf[3] == 0x05)
                {
                    sleep(HZ/3);
                    unsigned char data[] = {0x05, 0x02};
                    iap_send_pkt(data, sizeof(data));
                }
                /* FM remote sends this: */
                /* FF 55 03 00 01 02 FA (1st thing sent) */
                else if(serbuf[3] == 0x02)
                {
                    /* useful only for apple firmware */
                }
                break;
            }

            /* default response is with cmd ok packet */
            default:
            {
                unsigned char data[] = {0x00, 0x02, 0x00, 0x00};
                data[3] = serbuf[2]; /* respond with cmd */
                iap_send_pkt(data, sizeof(data));
                break;
            }
        }
    }
    /* Handle Mode 2 */
    else if (serbuf[1] == 0x02)
    {
        if(serbuf[2] != 0) return;
        iap_remotebtn = BUTTON_NONE;
        iap_remotetick = false;
        
        if(serbuf[0] >= 3 && serbuf[3] != 0)
        {
            if(serbuf[3] & 1)
                iap_remotebtn |= BUTTON_RC_PLAY;
            if(serbuf[3] & 2)
                iap_remotebtn |= BUTTON_RC_VOL_UP;
            if(serbuf[3] & 4)
                iap_remotebtn |= BUTTON_RC_VOL_DOWN;
            if(serbuf[3] & 8)
                iap_remotebtn |= BUTTON_RC_RIGHT;
            if(serbuf[3] & 16)
                iap_remotebtn |= BUTTON_RC_LEFT;
        }
        else if(serbuf[0] >= 4 && serbuf[4] != 0)
        {
            if(serbuf[4] & 1) /* play */
            {
                if (audio_status() != AUDIO_STATUS_PLAY)
                {
                    iap_remotebtn |= BUTTON_RC_PLAY;
                    iap_repeatbtn = 2;
                    iap_remotetick = false;
                    iap_changedctr = 1;
                }
            }
            if(serbuf[4] & 2) /* pause */
            {
                if (audio_status() == AUDIO_STATUS_PLAY)
                {
                    iap_remotebtn |= BUTTON_RC_PLAY;
                    iap_repeatbtn = 2;
                    iap_remotetick = false;
                    iap_changedctr = 1;
                }
            }
            if((serbuf[4] & 128) && !iap_btnshuffle) /* shuffle */
            {
                iap_btnshuffle = true;
                if(!global_settings.playlist_shuffle)
                {
                    global_settings.playlist_shuffle = 1;
                    settings_save();
                    if (audio_status() & AUDIO_STATUS_PLAY)
                        playlist_randomise(NULL, current_tick, true);
                }
                else if(global_settings.playlist_shuffle)
                {
                    global_settings.playlist_shuffle = 0;
                    settings_save();
                    if (audio_status() & AUDIO_STATUS_PLAY)
                        playlist_sort(NULL, true);
                }
            }
            else
                iap_btnshuffle = false;
        }
        else if(serbuf[0] >= 5 && serbuf[5] != 0)
        {
            if((serbuf[5] & 1) && !iap_btnrepeat) /* repeat */
            {
                int oldmode = global_settings.repeat_mode;
                iap_btnrepeat = true;
            
                if (oldmode == REPEAT_ONE)
                        global_settings.repeat_mode = REPEAT_OFF;
                else if (oldmode == REPEAT_ALL)
                        global_settings.repeat_mode = REPEAT_ONE;
                else if (oldmode == REPEAT_OFF)
                        global_settings.repeat_mode = REPEAT_ALL;

                settings_save();
                if (audio_status() & AUDIO_STATUS_PLAY)
                audio_flush_and_reload_tracks();
            }
            else
                iap_btnrepeat = false;

            if(serbuf[5] & 16) /* ffwd */
            {
                iap_remotebtn |= BUTTON_RC_RIGHT;
            }
            if(serbuf[5] & 32) /* frwd */
            {
                iap_remotebtn |= BUTTON_RC_LEFT;
            }
        }
    }
    /* Handle Mode 3 */
    else if (serbuf[1] == 0x03)
    {
        switch(serbuf[2])
        {
            /* some kind of status packet? */
            case 0x01:
            {
                unsigned char data[] = {0x03, 0x02, 0x00, 0x00, 0x00, 0x00};
                iap_send_pkt(data, sizeof(data));
                break;
            }

            case 0x08:
            {
                /* ACK */
                unsigned char data[] = {0x03, 0x00, 0x00, 0x08};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            
            case 0x0C:
            {
                /* request ipod volume */
                if (serbuf[3] == 0x04)
                {
                    iap_set_remote_volume();
                }
                break;
            }
            /* get volume from accessory */
            case 0x0E:
                if (serbuf[3] == 0x04)
                    global_settings.volume = (-58)+((int)serbuf[5]+1)/4;
                    sound_set_volume(global_settings.volume);
                break;
        }
    }
    /* Handle Mode 4 */
    else if (serbuf[1] == 0x04)
    {
        switch (((unsigned long)serbuf[2] << 8) | serbuf[3])
        {
            /* Get data updated??? flag */
            case 0x0009:
            {
                unsigned char data[] = {0x04, 0x00, 0x0A, 0x00};
                data[3] = iap_updateflag ? 0 : 1;
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Set data updated??? flag */
            case 0x000B:
            {
                iap_updateflag = serbuf[4] ? 0 : 1;
                /* respond with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x0B};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get iPod size? */
            case 0x0012:
            {
                unsigned char data[] = {0x04, 0x00, 0x13, 0x01, 0x0B};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get count of given types */
            case 0x0018:
            {
                unsigned char data[] = {0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00};
                unsigned long num = 0;
                switch(serbuf[4]) /* type number */
                {
                    case 0x01: /* total number of playlists */
                        num = 1;
                        break;
                    case 0x05: /* total number of songs */
                        num = 1;
                }
                data[3] = num >> 24;
                data[4] = num >> 16;
                data[5] = num >> 8;
                data[6] = num;
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get time and status */
            case 0x001C:
            {
                unsigned char data[] = {0x04, 0x00, 0x1D, 0x00, 0x00, 0x00, 
                                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
                struct mp3entry *id3 = audio_current_track();
                unsigned long time_total = id3->length;
                unsigned long time_elapsed = id3->elapsed;
                int status = audio_status();
                data[3] = time_total >> 24;
                data[4] = time_total >> 16;
                data[5] = time_total >> 8;
                data[6] = time_total;
                data[7] = time_elapsed >> 24;
                data[8] = time_elapsed >> 16;
                data[9] = time_elapsed >> 8;
                data[10] = time_elapsed;
                if (status == AUDIO_STATUS_PLAY)
                    data[11] = 0x01; /* play */
                else if (status & AUDIO_STATUS_PAUSE)
                    data[11] = 0x02; /* pause */ 
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get current pos in playlist */
            case 0x001E:
            {
                unsigned char data[] = {0x04, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00};
                long playlist_pos = playlist_next(0);
                playlist_pos -= playlist_get_first_index(NULL);
                if(playlist_pos < 0)
                    playlist_pos += playlist_amount();
                data[3] = playlist_pos >> 24;
                data[4] = playlist_pos >> 16;
                data[5] = playlist_pos >> 8;
                data[6] = playlist_pos;
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get title of a song number */
            case 0x0020:
            /* Get artist of a song number */
            case 0x0022:
            /* Get album of a song number */
            case 0x0024:
            {
                unsigned char data[70] = {0x04, 0x00, 0xFF};
                struct mp3entry id3;
                int fd;
                size_t len;
                long tracknum = (signed long)serbuf[4] << 24 |
                                (signed long)serbuf[5] << 16 |
                                (signed long)serbuf[6] << 8 | serbuf[7];
                data[2] = serbuf[3] + 1;
                memcpy(&id3, audio_current_track(), sizeof(id3));
                tracknum += playlist_get_first_index(NULL);
                if(tracknum >= playlist_amount())
                    tracknum -= playlist_amount();

                /* If the tracknumber is not the current one,
                   read id3 from disk */
                if(playlist_next(0) != tracknum)
                {
                    struct playlist_track_info info;
                    playlist_get_track_info(NULL, tracknum, &info);
                    fd = open(info.filename, O_RDONLY);
                    memset(&id3, 0, sizeof(struct mp3entry));
                    get_metadata(&id3, fd, info.filename);
                    close(fd);
                }

                /* Return the requested track data */
                switch(serbuf[3])
                {
                    case 0x20:
                        len = strlcpy((char *)&data[3], id3.title, 64);
                            iap_send_pkt(data, 4+len);
                        break;
                    case 0x22:
                        len = strlcpy((char *)&data[3], id3.artist, 64);
                            iap_send_pkt(data, 4+len);
                        break;
                    case 0x24:
                        len = strlcpy((char *)&data[3], id3.album, 64);
                            iap_send_pkt(data, 4+len);
                        break;
                }
                break;
            }
            /* Set polling mode */
            case 0x0026:
            {
                iap_pollspeed = serbuf[4] ? 1 : 0;
                /*responsed with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x26};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* AiR playback control */
            case 0x0029:
            {
                /* respond with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x29};
                iap_send_pkt(data, sizeof(data));
                switch(serbuf[4])
                {
                    case 0x01: /* play/pause */
                        iap_remotebtn = BUTTON_RC_PLAY;
                        iap_repeatbtn = 2;
                        iap_remotetick = false;
                        iap_changedctr = 1;
                        break;
                    case 0x02: /* stop */
                        iap_remotebtn = BUTTON_RC_PLAY|BUTTON_REPEAT;
                        iap_repeatbtn = 2;
                        iap_remotetick = false;
                        iap_changedctr = 1;
                        break;
                    case 0x03: /* skip++ */
                        iap_remotebtn = BUTTON_RC_RIGHT;
                        iap_repeatbtn = 2;
                        iap_remotetick = false;
                        break;
                    case 0x04: /* skip-- */
                        iap_remotebtn = BUTTON_RC_LEFT;
                        iap_repeatbtn = 2;
                        iap_remotetick = false;
                        break;
                    case 0x05: /* ffwd */
                        iap_remotebtn = BUTTON_RC_RIGHT;
                        iap_remotetick = false;
                        if(iap_pollspeed) iap_pollspeed = 5;
                        break;
                    case 0x06: /* frwd */
                        iap_remotebtn = BUTTON_RC_LEFT;
                        iap_remotetick = false;
                        if(iap_pollspeed) iap_pollspeed = 5;
                        break;
                    case 0x07: /* end ffwd/frwd */
                        iap_remotebtn = BUTTON_NONE;
                        iap_remotetick = false;
                        if(iap_pollspeed) iap_pollspeed = 1;
                        break;
                }
                break;
            }
            /* Get shuffle mode */
            case 0x002C:
            {
                unsigned char data[] = {0x04, 0x00, 0x2D, 0x00};
                data[3] = global_settings.playlist_shuffle ? 1 : 0;
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Set shuffle mode */
            case 0x002E:
            {
                if(serbuf[4] && !global_settings.playlist_shuffle)
                {
                    global_settings.playlist_shuffle = 1;
                    settings_save();
                    if (audio_status() & AUDIO_STATUS_PLAY)
                        playlist_randomise(NULL, current_tick, true);
                }
                else if(!serbuf[4] && global_settings.playlist_shuffle)
                {
                    global_settings.playlist_shuffle = 0;
                    settings_save();
                    if (audio_status() & AUDIO_STATUS_PLAY)
                        playlist_sort(NULL, true);
                }

                
                /* respond with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x2E};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get repeat mode */
            case 0x002F:
            {
                unsigned char data[] = {0x04, 0x00, 0x30, 0x00};
                if(global_settings.repeat_mode == REPEAT_OFF)
                    data[3] = 0;
                else if(global_settings.repeat_mode == REPEAT_ONE)
                    data[3] = 1;
                else
                    data[3] = 2;
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Set repeat mode */
            case 0x0031:
            {
                int oldmode = global_settings.repeat_mode;
                if (serbuf[4] == 0)
                    global_settings.repeat_mode = REPEAT_OFF;
                else if (serbuf[4] == 1)
                    global_settings.repeat_mode = REPEAT_ONE;
                else if (serbuf[4] == 2)
                    global_settings.repeat_mode = REPEAT_ALL;

                if (oldmode != global_settings.repeat_mode)
                {
                    settings_save();
                    if (audio_status() & AUDIO_STATUS_PLAY)
                        audio_flush_and_reload_tracks();
                }

                /* respond with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x31};
                iap_send_pkt(data, sizeof(data));
                break;
            }        
            /* Get Screen Size */
            case 0x0033:
            {
                unsigned char data[] = {0x04, 0x00, 0x34,
                                        LCD_WIDTH >> 8, LCD_WIDTH & 0xff,
                                        LCD_HEIGHT >> 8, LCD_HEIGHT & 0xff,
                                        0x01};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* Get number songs in current playlist */
            case 0x0035:
            {
                unsigned char data[] = {0x04, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00};
                unsigned long playlist_amt = playlist_amount();
                data[3] = playlist_amt >> 24;
                data[4] = playlist_amt >> 16;
                data[5] = playlist_amt >> 8;
                data[6] = playlist_amt;
                iap_send_pkt(data, sizeof(data));
                break;
            }                
            /* Jump to track number in current playlist */
            case 0x0037:
            {
                int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
                long tracknum = (signed long)serbuf[4] << 24 |
                                (signed long)serbuf[5] << 16 |
                                (signed long)serbuf[6] << 8 | serbuf[7];
                audio_pause();
                audio_skip(tracknum - playlist_next(0));
                if (!paused)
                    audio_resume();
                
                /* respond with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
                data[4] = serbuf[2];
                data[5] = serbuf[3];
                iap_send_pkt(data, sizeof(data));
                break;
            }
            default:
            {
                /* default response is with cmd ok packet */
                unsigned char data[] = {0x04, 0x00, 0x01, 0x00, 0x00, 0x00};
                data[4] = serbuf[2];
                data[5] = serbuf[3];
                iap_send_pkt(data, sizeof(data));
                break;
            }
        }
    }
    /* Handle Mode 7 */
    else if (serbuf[1] == 0x07)
    {
        switch(serbuf[2])
        {
            /* tuner capabilities */
            case 0x02:
            {
                /* do nothing */
                
                unsigned char data[] = {0x00, 0x27, 0x00};
                iap_send_pkt(data, sizeof(data));
                break;
            }
            /* actual tuner frequency */
            case 0x0A:
            /* fall through */
            /* tuner frequency from scan */
            case 0x13:
            {
                rmt_tuner_freq();
                break;
            }
            /* RDS station name 0x21 1E 00 + ASCII text*/
            case 0x21:
            {
                rmt_tuner_rds_data();
                break;
            }
        }
    }
    serbuf[0] = 0;
}

int remote_control_rx(void)
{
    int btn = iap_remotebtn;
    if(iap_repeatbtn)
    {
        iap_repeatbtn--;
        if(!iap_repeatbtn)
        {
            iap_remotebtn = BUTTON_NONE;
            iap_remotetick = true;
        }
    }
    else
        iap_remotetick = true;

    return btn;
}
