/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * 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 "iap-core.h"
#include "iap-lingo.h"
#include "kernel.h"
#include "system.h"
#include "tuner.h"
#if CONFIG_TUNER
#include "ipod_remote_tuner.h"
#endif

/*
 * This macro is meant to be used inside an IAP mode message handler.
 * It is passed the expected minimum length of the message inbufferfer.
 * If the inbufferfer does not have the required lenght an ACK
 * packet with a Bad Parameter error is generated.
 */
#define CHECKLEN(x) do { \
        if (len < (x)) { \
            cmd_ack(cmd, IAP_ACK_BAD_PARAM); \
            return; \
        }} while(0)

/* Check for authenticated state, and return an ACK Not
 * Authenticated on failure.
 */
#define CHECKAUTH do { \
        if (!DEVICE_AUTHENTICATED) { \
            cmd_ack(cmd, IAP_ACK_NO_AUTHEN); \
            return; \
        }} while(0)


static void cmd_ack(const unsigned char cmd, const unsigned char status)
{
    IAP_TX_INIT(0x07, 0x00);
    IAP_TX_PUT(status);
    IAP_TX_PUT(cmd);

    iap_send_tx();
}

#define cmd_ok(cmd) cmd_ack((cmd), IAP_ACK_OK)

void iap_handlepkt_mode7(const unsigned int len, const unsigned char *inbuffer)
{
    /* Note that some of the Lingo Mode 7 commands are handled by
     * ../firmware/drivers/tuner/ipod_remote_tuner.c as some of the
     * commands are sourced with the remote as the master with the ipod acting
     * as the slave.
     */
    unsigned char cmd = inbuffer[1];
    unsigned char statusnotifymaskbyte = 0;

    /* We expect at least two bytes in the inbuffer, one for the
     * lingo and one for the command
     */
    CHECKLEN(2);

    /* Lingo 0x07 must have been negotiated */
    if (!DEVICE_LINGO_SUPPORTED(0x07)) {
        cmd_ack(cmd, IAP_ACK_BAD_PARAM);
        return;
    }

    switch (cmd)
    {
        /* case 00 ToIpod      Ack 2/6 bytes*/
        case 0x00:
        {
            /* 0x00 OK
             * 0x01 Unknown Track Category
             * 0x02 Command Failed. Command is valid but did not succeed
             * 0x03 Out Of Resources
             * 0x04 Bad Parameter
             * 0x05 Unknown Track ID
             * 0x06 Command Pending.
             * 0x07 Not Authenticated
             *
             * byte  1 is ID of command being acknowledged
             * bytes 2-5 only if status byte is pending. timeout in ms.
             */
             break;
        }

        /* case 0x01 ToAccessory GetTunerCaps
         * This is sent by iap-lingo0.c through case 0x15 after device
         * has been authenticated FF55020701F6
         */

        /* case 02 ToIpod      RetTunerCaps 8 bytes */
        case 0x02:
        {
            /* Capabilities are stored as bits in first 4 bytes,
             * inbuffer[2] byte is bits 31:24
             * inbuffer[3] byte is bits 23:16
             * inbuffer[4] byte is bits 15:08
             * inbuffer[5] byte is bits 07:00
             * inbuffer[6] and inbuffer[7] are all reserved bits
             * Bit 0 = AM Band 520-1710 Khz
             * Bit 1 = FM Europe/US 87.5 - 108.0 Mhz
             * Bit 2 = FM Japan 76.0 - 90.0 Mhz
             * Bit 3 = FM Wide 76.0 - 108.0 Mhz
             * Bit 4 = HD Radio Capable
             * Bit 5:7 Reserved
             * Bit 8 = Tuner Power On/Off Control Capable
             * Bit 9 = Status Change Notification Capable
             * Bit 10:15 Reserved
             * Bit 17:16 Minimum FM Resolution ID Bits
             *     00 = 200Khz, 01 = 100Khz, 10 = 50Khz, 11 = reserved
             * Bit 18 = Tuner Seek Up/Down Capable
             * Bit 19 = Tuner Seek RSSI Threshold. Only if 18=1
             * Bit 20 = Force Monophonic mode capable
             * Bit 21 = Stero Blend Capable
             * Bit 22 = FM Tuner deemphasis select capable
             * Bit 23 = AM Tuner Resolution 9Khz (0=10Khz Only) capable
             * Bit 24 = Radio Data System (RDS/RBDS) data capable
             * Bit 25 = Tuner Channel RSSI indicator capable
             * Bit 26 = Stero Source Indicator capable
             * Bit 27 = RDS/RBDS Raw mode capable
             * Bit 31:28 Reserved
             *
             * ipod Tuner returns 07 5E 07 0E 10 4B
             * Bytes 6,7 Reserved
             * ???????? ????????
             * ???????? ????????
             * 00010000 01001011
             *
             *  Byte 5 - 0E
             * 00000000
             * 76543210
             * 00001110
             *        AM
             *       FM Europe/US
             *      FM Japan
             *     FM Wide
             *
             * Byte 4 - 07
             * 11111100
             * 54321098
             * 00000111
             *        Tuner Power On/Off
             *       Status Change Notification
             *      ?? Should be reserved
             *
             * Byte 3 - 5E
             * 22221111
             * 32109876
             * 01011110
             *       Tuner Seek Up/Down
             *      Tuner Seek RSSI Threshold
             *     Force Mono Mode Capable
             *    Stereo Blend Capable
             *  FM Tuner deemphasis select capable
             *
             * Byte 2 - 07
             * 33222222
             * 10987654
             * 00000111
             *        RDS/RBDS Capable
             *       Tuner Channel RSSI Indicator
             *      Stereo Source
             *
             * Just need to see what we can use this data for
             * Make a selection for the tuner mode to select
             * Preference is
             * 1st - 76 to 108 FM
             * 2nd - 87.5 to 108 Fm
             * 3rd - 76 to 90 Fm
             * 4th - AM
             *
             */

            if ((inbuffer[4] & 0x03) >0) {
                statusnotifymaskbyte = 0;
                if ((inbuffer[4] >> 0) & 0x01) {
                    /* Supports Tuner Power On/Off, so set ON */
                    statusnotifymaskbyte = 1;
                }
                if ((inbuffer[4] >> 1) & 0x01) {
                    /* Supports Status Change Notification so set ON  */
                    /* Apple 5/6/7G firmware does NOT enable this bit */
                    /* statusnotifymaskbyte += 2;                     */
                }
                IAP_TX_INIT(0x07, 0x05);
                IAP_TX_PUT(statusnotifymaskbyte);
                iap_send_tx();
            }
            if ((inbuffer[5] >> 1) & 0x01) {
                /* Supports FM Europe/US Tuner 87.5 - 108.0 Mhz */
                /* Apple firmware sends this before setting region */
                IAP_TX_INIT(0x07, 0x0E);
                IAP_TX_PUT(0x00);
                iap_send_tx();
                /* Apple firmware then sends region */
                IAP_TX_INIT(0x07, 0x08);
                IAP_TX_PUT(0x02);
                iap_send_tx();
            } else if ((inbuffer[5] >> 3) & 0x01) {
                /* Supports FM Wide Tuner 76 - 108.0 Mhz */
                /* apple firmware send this before setting region */
                IAP_TX_INIT(0x07, 0x0E);
                IAP_TX_PUT(0x00);
                iap_send_tx();
                /* Apple firmware then send region */
                IAP_TX_INIT(0x07, 0x08);
                IAP_TX_PUT(0x08);
                iap_send_tx();
            } else if ((inbuffer[5] >> 2) & 0x01) {
                /* Supports FM Japan Tuner 76 - 90.0 Mhz */
                /* apple firmware send this before setting region */
                IAP_TX_INIT(0x07, 0x0E);
                IAP_TX_PUT(0x41);
                iap_send_tx();
                /* Apple firmware then send region */
                IAP_TX_INIT(0x07, 0x08);
                IAP_TX_PUT(0x04);
                iap_send_tx();
            } else if ((inbuffer[5] >> 0) & 0x01) {
                /* Supports AM Tuner */
                IAP_TX_INIT(0x07, 0x08);
                IAP_TX_PUT(0x01);
                iap_send_tx();
            }

            if ((inbuffer[2]  & 0x03) > 0) {
                statusnotifymaskbyte = 0;
                if ((inbuffer[2] >> 0) & 0x01) {
                   /* Supports RDS/RBDS Capable so set
                    *StatusChangeNotify for RDS/RBDS Data
                    */
                    statusnotifymaskbyte = 1;
                }
                if ((inbuffer[2] >> 1) & 0x01) {
                    /* Supports Tuner Channel RSSi Indicator Capable so set */
                    /* StatusChangeNotify for RSSI */
                    /* Apple 5G firmware does NOT enable this bit so we wont  */
                    /* statusnotifymaskbyte += 2;                   */
                }
                IAP_TX_INIT(0x07, 0x18);
                IAP_TX_PUT(statusnotifymaskbyte);
                iap_send_tx();
            }

            if ((inbuffer[4] >> 2) & 0x01) {
                /* Reserved */
            }
            if ((inbuffer[4] >> 3) & 0x01) {
                /* Reserved */
            }
            if ((inbuffer[3] >> 1) & 0x01) {
                /* Tuner Seek Up/Down` */
            }
            if ((inbuffer[3] >> 2) & 0x01) {
                /* Tuner Seek RSSI Threshold */
            }
            if ((inbuffer[3] >> 3) & 0x01) {
                /* Force Mono Mode */
            }
            if ((inbuffer[3] >> 4) & 0x01) {
                /* Stereo Blend */
            }
            if ((inbuffer[3] >> 6) & 0x01) {
                /* FM Tuner deemphasis */
            }
            if ((inbuffer[2] >> 2) & 0x01) {
                /* Stereo Source */
            }
            break;
        }
        /* case 03 ToAccessory GetTunerCtrl 2 bytes */

        /* case 04 ToIpod      RetTunerCtrl 3 bytes
         * Bit 0 power is on (1) or Off (0)
         * Bit 1 StatusChangeNotify is enabled (1) or disabled (0)
         * Bit 3 RDS/RBDS Raw mode enabled
         *
         * Should/Can we do something with these?
         */

        /* case 05 ToAccessory SetTunerCtrl 3 bytes
         *         Bits as per 0x04 above
         *         Bit 0/1 set through Lingo7 Cmd02 */

        /* case 06 ToAccessory GetTunerBand 2 bytes */

        /* case 07 ToIpod      RetTunerBand 3 bytes
         * Returns current band for Tuner. See 0x08 below
         *
         * Should/Can we do something with these?
         */

        /* case 08 ToAccessory SetTuneBand
         *         Set Bit 0 for AM
         *         Set Bit 1 for FM Europe/U S 87.5-108Mhz
         *         Set Bit 2 for FM JApan 76.0-90.0Mhz
         *         Set Bit 3 for FM Wide 76.0-108Mhz
         *         Currently we send this after receiving capabilities
         *         on 0x02 above
         */

        /* case 09 ToAccessory GetTunerFreq 2 bytes */

        /* case 0A ToIpod      RetTunerFreq 7 bytes */
        case 0x0A:
        {
            /* Returns Frequency set and RSSI Power Levels
             * These are sent as is to rmt_tuner_freq() in
             * ../firmware/drivers/tuner/ipod_remote_tuner.c */
            rmt_tuner_freq(len, inbuffer);
            break;
        }

        /* case 0B ToAccessory SetTunerFreq 6 bytes */

        /* case 0C ToAccessory GetTunerMode 2 bytes */

        /* case 0D ToIpod      RetTunerMode 3 bytes
         * Returns Tuner Mode Status in 8 bits as follows
         * Bit 1:0 - FM Tuner Resolution
         * Bit 2 Tuner is seeking up or down
         * Bit 3 Tuner is seeking with RSSI min theshold enabled
         * Bit 4 Force Mono Mode (1) or allow stereo (0)
         * Bit 5 Stereo Blend enabled. Valid only if Bit 4 is 0
         * Bit 6 FM Tuner Deemphasis 50uS (1) or 75uS (0)
         * Bit 7 Reserved 0
         */

        /* case 0E ToAccessory SetTunerMode 3 bytes
         *         See 0x0D for Bit Descriptions
         * Bits set by Cmd 02
         */

        /* case 0F ToAccessory GetTunerSeekRssi 2 bytes */

        /* case 10 ToIpod      RetTunerSeekRssi 3 bytes
         * Returns RSSI Value for seek operations
         * value is 0 (min) - 255 (max)
         */

        /* case 11 ToAccessory SetTunerSeekRssi 3 bytes */

        /* case 12 ToAccessory TunerSeekStart 3 bytes */

        /* case 13 ToIpod      TunerSeekDone 7 bytes */
        case 0x13:
        {
            rmt_tuner_freq(len, inbuffer);
            break;
        }

        /* case 14 ToAccessory GetTunerStatus 2 bytes */

        /* case 15 ToIpod      RetTunerStatus 3 bytes */

        /* case 16 ToAccessory GetStatusNotifyMask 2 bytes */

        /* case 17 ToIpod      RetStatusNotifyMask 3 bytes */

        /* case 18 ToAccessory SetStatusNotifyMask 3 bytes
         * This is set by Cmd 02
         */

        /* case 19 ToIpod      StatusChangeNotify 3 bytes */
        case 0x19:
        {
            /* Returns StatusChangeNotify bits to ipod.
             * Bit 0 set for RDS/RBDS data ready
             * Bit 1 set for Tuner RSSI level change
             * Bit 2 for Stereo Indicator changed
             * If any of these are set we will request the data
             * need to look at using these
             */
             break;
         }

        /* case 1A ToAccessory GetRdsReadyStatus 2 bytes */

        /* case 1B ToIpod      RetRdsReadyStatus 6 bytes */
        case 0x1B:
        {
            break;
        }
        /* case 1C ToAccessory GetRdsData 3 bytes */

        /* case 1D ToIpod      RetRdsData NN bytes */
        case 0x1D:
        {
            rmt_tuner_rds_data(len, inbuffer);
            break;
        }

        /* case 1E ToAccessory GetRdsNotifyMask 2 bytes*/

        /* case 1F ToIpod      RetRdsNotifyMask 6 Bytes*/
        case 0x1F:
        {
            break;
        }

        /* case 20 ToAccessory SetRdsNotifyMask 6 bytes */

        /* case 21 ToIpod      RdsReadyNotify NN bytes */
        case 0x21:
        {
            rmt_tuner_rds_data(len, inbuffer);
            break;
        }
        /* case 22 Reserved */

        /* case 23 Reserved */

        /* case 24 Reserved */

        /* case 25 ToAccessory GetHDProgramServiceCount 0 bytes */

        /* case 26 ToIpod      RetHDProgramServiceCount 1 bytes */
        case 0x26:
        {
            break;
        }

        /* case 27 ToAccessory GetHDProgramService 0  bytes */

        /* case 28 ToIpod      RetHDProgramService 1 bytes */
        case 0x28:
        {
            break;
        }

        /* case 29 ToAccessory SetHDProgramService 1 bytes */

        /* case 2A ToAccessory GetHDDataReadyStatus 0 bytes */

        /* case 2B ToIpod      RetHDDataReadyStatus 4 bytes */
        case 0x2B:
        {
            break;
        }

        /* case 2C ToAccessory GetHDData 1 bytes */

        /* case 2D ToIpod      RetHDData NN bytes */
        case 0x2D:
        {
            break;
        }

        /* case 2E ToAccessory GetHDDataNotifyMask 0 bytes */

        /* case 2F ToIpod      RetHDDataNotifyMask 4 bytes */
        case 0x2F:
        {
            break;
        }

        /* case 30 ToAccessory SetHDDataNotifyMask 4 bytes */

        /* case 31 ToIpod      HDDataReadyNotify NN bytes */
        case 0x31:
        {
            break;
        }

        /* The default response is IAP_ACK_BAD_PARAM */
        default:
        {
#ifdef LOGF_ENABLE
            logf("iap: Unsupported Mode07 Command");
#endif
            cmd_ack(cmd, IAP_ACK_BAD_PARAM);
            break;
        }
    }
}
