/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2002 by Daniel Stenberg
 *
 * iPod driver based on code from the ipodlinux project - http://ipodlinux.org
 * Adapted for Rockbox in December 2005
 * Original file: linux/arch/armnommu/mach-ipod/keyboard.c
 * Copyright (c) 2003-2005 Bernard Leach (leachbj@bouncycastle.org)
 *
 *
 * 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.
 *
 ****************************************************************************/

/*
 * Rockbox button functions
 */

#include <stdlib.h>
#include "config.h"
#include "cpu.h"
#include "system.h"
#include "button.h"
#include "kernel.h"
#include "backlight.h"
#include "adc.h"
#include "serial.h"
#include "power.h"
#include "system.h"
#include "powermgmt.h"

#ifdef HAVE_REMOTE_LCD
#include "lcd-remote.h"
#endif

#ifdef TARGET_TREE
#include "button-target.h"
#endif

struct event_queue button_queue;

static long lastbtn;   /* Last valid button status */
static long last_read; /* Last button status, for debouncing/filtering */
#ifdef HAVE_LCD_BITMAP
static bool flipped;  /* buttons can be flipped to match the LCD flip */
#endif
#ifdef CONFIG_BACKLIGHT
static bool filter_first_keypress;
#ifdef HAVE_REMOTE_LCD
static bool remote_filter_first_keypress;
#endif
#endif

/* how long until repeat kicks in, in ticks */
#define REPEAT_START      30

/* the speed repeat starts at, in ticks */
#define REPEAT_INTERVAL_START   16

/* speed repeat finishes at, in ticks */
#define REPEAT_INTERVAL_FINISH  5

/* the power-off button and number of repeated keys before shutting off */
#if (CONFIG_KEYPAD == IPOD_3G_PAD) || (CONFIG_KEYPAD == IPOD_4G_PAD) ||\
    (CONFIG_KEYPAD == IRIVER_IFP7XX_PAD)
#define POWEROFF_BUTTON BUTTON_PLAY
#define POWEROFF_COUNT 40
#elif !defined(TARGET_TREE)
#define POWEROFF_BUTTON BUTTON_OFF
#define POWEROFF_COUNT 10
#endif

static int button_read(void);

#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
static bool remote_button_hold_only(void);
#endif

#if CONFIG_KEYPAD == IPOD_4G_PAD
/* Variable to use for setting button status in interrupt handler */
int int_btn = BUTTON_NONE;
#endif

#if (CONFIG_KEYPAD == IPOD_4G_PAD) && !defined(IPOD_MINI)
static void opto_i2c_init(void)
{
    int i, curr_value;

    /* wait for value to settle */
    i = 1000;
    curr_value = (inl(0x7000c104) << 16) >> 24;
    while (i > 0)
    {
        int new_value = (inl(0x7000c104) << 16) >> 24;

        if (new_value != curr_value) {
            i = 10000;
            curr_value = new_value;
        }
        else {
            i--;
        }
    }

    GPIOB_OUTPUT_VAL |= 0x10;
    DEV_EN |= 0x10000;
    DEV_RS |= 0x10000;
    udelay(5);
    DEV_RS &= ~0x10000; /* finish reset */

    outl(0xffffffff, 0x7000c120);
    outl(0xffffffff, 0x7000c124);
    outl(0xc00a1f00, 0x7000c100);
    outl(0x1000000, 0x7000c104);
}

static inline int ipod_4g_button_read(void)
{
    /* The ipodlinux source had a udelay(250) here, but testing has shown that
       it is not needed - tested on Nano, Color/Photo and Video. */
    /* udelay(250);*/

    int btn = BUTTON_NONE;
    unsigned reg = 0x7000c104;
    if ((inl(0x7000c104) & 0x4000000) != 0) {
        unsigned status = inl(0x7000c140);

        reg = reg + 0x3C;      /* 0x7000c140 */
        outl(0x0, 0x7000c140); /* clear interrupt status? */

        if ((status & 0x800000ff) == 0x8000001a) {
            static int old_wheel_value IDATA_ATTR = -1;
            static int wheel_repeat = 0;

            if (status & 0x100)
                btn |= BUTTON_SELECT;
            if (status & 0x200)
                btn |= BUTTON_RIGHT;
            if (status & 0x400)
                btn |= BUTTON_LEFT;
            if (status & 0x800)
                btn |= BUTTON_PLAY;
            if (status & 0x1000)
                btn |= BUTTON_MENU;
            if (status & 0x40000000) {
                /* NB: highest wheel = 0x5F, clockwise increases */
                int new_wheel_value = (status << 9) >> 25;
                backlight_on();
                /* The queue should have no other events when scrolling */
                if (queue_empty(&button_queue) && old_wheel_value >= 0) {

                    /* This is for later = BUTTON_SCROLL_TOUCH;*/
                    int wheel_delta = new_wheel_value - old_wheel_value;
                    unsigned long data;
                    int wheel_keycode;

                    if (wheel_delta < -48)
                        wheel_delta += 96; /* Forward wrapping case */
                    else if (wheel_delta > 48)
                        wheel_delta -= 96; /* Backward wrapping case */

                    if (wheel_delta > 4) {
                        wheel_keycode = BUTTON_SCROLL_FWD;
                    } else if (wheel_delta < -4) {
                        wheel_keycode = BUTTON_SCROLL_BACK;
                    } else goto wheel_end;

                    data = (wheel_delta << 16) | new_wheel_value;
                    queue_post(&button_queue, wheel_keycode | wheel_repeat,
                            (void *)data);

                    if (!wheel_repeat) wheel_repeat = BUTTON_REPEAT;
                }

                old_wheel_value = new_wheel_value;
            } else if (old_wheel_value >= 0) {
                /* scroll wheel up */
                old_wheel_value = -1;
                wheel_repeat = 0;
            }

        } else if (status == 0xffffffff) {
            opto_i2c_init();
        }
    }

wheel_end:

    if ((inl(reg) & 0x8000000) != 0) {
        outl(0xffffffff, 0x7000c120);
        outl(0xffffffff, 0x7000c124);
    }
    return btn;
}

void ipod_4g_button_int(void)
{
    CPU_HI_INT_CLR = I2C_MASK;
    /* The following delay was 250 in the ipodlinux source, but 50 seems to 
       work fine - tested on Nano, Color/Photo and Video. */
    udelay(50); 
    outl(0x0, 0x7000c140); 
    int_btn = ipod_4g_button_read();
    outl(inl(0x7000c104) | 0xC000000, 0x7000c104);
    outl(0x400a1f00, 0x7000c100);

    GPIOB_OUTPUT_VAL |= 0x10;
    CPU_INT_EN = 0x40000000;
    CPU_HI_INT_EN = I2C_MASK;
}
#endif 
#if (CONFIG_KEYPAD == IPOD_3G_PAD) || defined(IPOD_MINI)
/* iPod 3G and mini 1G, mini 2G uses iPod 4G code */
void handle_scroll_wheel(int new_scroll, int was_hold, int reverse)
{
    int wheel_keycode = BUTTON_NONE;
    static int prev_scroll = -1;
    static int direction = 0;
    static int count = 0;
    static int scroll_state[4][4] = {
        {0, 1, -1, 0},
        {-1, 0, 0, 1},
        {1, 0, 0, -1},
        {0, -1, 1, 0}
    };

    if ( prev_scroll == -1 ) {
        prev_scroll = new_scroll;
    }
    else if (direction != scroll_state[prev_scroll][new_scroll]) {
        direction = scroll_state[prev_scroll][new_scroll];
        count = 0;
    }
    else if (!was_hold) {
        backlight_on();
        if (++count == 6) { /* reduce sensitivity */
            count = 0;
            switch (direction) {
                case 1:
                    if (reverse) {
                        /* 'r' keypress */
                        wheel_keycode = BUTTON_SCROLL_FWD;
                    }
                    else {
                        /* 'l' keypress */
                        wheel_keycode = BUTTON_SCROLL_BACK;
                    }
                    break;
                case -1:
                    if (reverse) {
                        /* 'l' keypress */
                        wheel_keycode = BUTTON_SCROLL_BACK;
                    }
                    else {
                        /* 'r' keypress */
                        wheel_keycode = BUTTON_SCROLL_FWD;
                    }
                    break;
                default:
                    /* only happens if we get out of sync */
                    break;
            }
        }
    }
    if (wheel_keycode != BUTTON_NONE && queue_empty(&button_queue))
        queue_post(&button_queue, wheel_keycode, NULL);
    prev_scroll = new_scroll;
}
#endif
#if (CONFIG_KEYPAD == IPOD_4G_PAD) && defined(IPOD_MINI)
/* mini 1 only, mini 2G uses iPod 4G code */
static int ipod_mini_button_read(void)
{
    unsigned char source, wheel_source, state, wheel_state;
    static bool was_hold = false;
    int btn = BUTTON_NONE;

    /* The ipodlinux source had a udelay(250) here, but testing has shown that
       it is not needed - tested on mini 1g. */
    /* udelay(250);*/

    /* get source(s) of interupt */
    source = GPIOA_INT_STAT & 0x3f;
    wheel_source = GPIOB_INT_STAT & 0x30;

    if (source == 0 && wheel_source == 0) {
        return BUTTON_NONE; /* not for us */
    }

    /* get current keypad & wheel status */
    state = GPIOA_INPUT_VAL & 0x3f;
    wheel_state = GPIOB_INPUT_VAL & 0x30;

    /* toggle interrupt level */
    GPIOA_INT_LEV = ~state;
    GPIOB_INT_LEV = ~wheel_state;

    /* hold switch causes all outputs to go low    */
    /* we shouldn't interpret these as key presses */
    if ((state & 0x20)) {
        if (!(state & 0x1))
            btn |= BUTTON_SELECT;
        if (!(state & 0x2))
            btn |= BUTTON_MENU;
        if (!(state & 0x4))
            btn |= BUTTON_PLAY;
        if (!(state & 0x8))
            btn |= BUTTON_RIGHT;
        if (!(state & 0x10))
            btn |= BUTTON_LEFT;

        if (wheel_source & 0x30) {
            handle_scroll_wheel((wheel_state & 0x30) >> 4, was_hold, 1);
        }
    }

    was_hold = button_hold();

    /* ack any active interrupts */
    if (source)
        GPIOA_INT_CLR = source;
    if (wheel_source)
        GPIOB_INT_CLR = wheel_source;

    return btn;
}

void ipod_mini_button_int(void)
{
    CPU_HI_INT_CLR = GPIO_MASK;
    int_btn = ipod_mini_button_read();
    //CPU_INT_EN = 0x40000000;
    CPU_HI_INT_EN = GPIO_MASK;
}
#endif
#if CONFIG_KEYPAD == IPOD_3G_PAD
static int ipod_3g_button_read(void)
{
    unsigned char source, state;
    static int was_hold = 0;
    int btn = BUTTON_NONE;
    /*
     * we need some delay for g3, cause hold generates several interrupts,
     * some of them delayed
     */
    udelay(250);

    /* get source of interupts */
    source = GPIOA_INT_STAT;


    /* get current keypad status */
    state = GPIOA_INPUT_VAL;
    GPIOA_INT_LEV = ~state;

    if (was_hold && source == 0x40 && state == 0xbf) {
        /* ack any active interrupts */
        GPIOA_INT_CLR = source;
        return BUTTON_NONE;
    }
    was_hold = 0;


    if ((state & 0x20) == 0) {
        /* 3g hold switch is active low */
        was_hold = 1;
        /* hold switch on 3g causes all outputs to go low */
        /* we shouldn't interpret these as key presses */
        GPIOA_INT_CLR = source;
        return BUTTON_NONE;
    }
    if ((state & 0x1) == 0) {
        btn |= BUTTON_RIGHT;
    }
    if ((state & 0x2) == 0) {
        btn |= BUTTON_SELECT;
    }
    if ((state & 0x4) == 0) {
        btn |= BUTTON_PLAY;
    }
    if ((state & 0x8) == 0) {
        btn |= BUTTON_LEFT;
    }
    if ((state & 0x10) == 0) {
        btn |= BUTTON_MENU;
    }

    if (source & 0xc0) {
        handle_scroll_wheel((state & 0xc0) >> 6, was_hold, 0);
    }

    /* ack any active interrupts */
    GPIOA_INT_CLR = source;

    return btn;
}
#endif

static void button_tick(void)
{
    static int count = 0;
    static int repeat_speed = REPEAT_INTERVAL_START;
    static int repeat_count = 0;
    static bool repeat = false;
    static bool post = false;
#ifdef CONFIG_BACKLIGHT
    static bool skip_release = false;
#ifdef HAVE_REMOTE_LCD
    static bool skip_remote_release = false;
#endif
#endif
    int diff;
    int btn;

#if (CONFIG_KEYPAD == PLAYER_PAD) || (CONFIG_KEYPAD == RECORDER_PAD)

    /* Post events for the remote control */
    btn = remote_control_rx();
    if(btn)
    {
        queue_post(&button_queue, btn, NULL);
    }
#endif

    btn = button_read();

    /* Find out if a key has been released */
    diff = btn ^ lastbtn;
    if(diff && (btn & diff) == 0)
    {
#ifdef CONFIG_BACKLIGHT
#ifdef HAVE_REMOTE_LCD
        if(diff & BUTTON_REMOTE)
            if(!skip_remote_release)
                queue_post(&button_queue, BUTTON_REL | diff, NULL);
            else
                skip_remote_release = false;
        else
#endif
            if(!skip_release)
                queue_post(&button_queue, BUTTON_REL | diff, NULL);
            else
                skip_release = false;
#else
        queue_post(&button_queue, BUTTON_REL | diff, NULL);
#endif
    }
    else
    {
        if ( btn )
        {
            /* normal keypress */
            if ( btn != lastbtn )
            {
                post = true;
                repeat = false;
                repeat_speed = REPEAT_INTERVAL_START;
            }
            else /* repeat? */
            {
                if ( repeat )
                {
                    if (!post)
                        count--;
                    if (count == 0) {
                        post = true;
                        /* yes we have repeat */
                        if (repeat_speed > REPEAT_INTERVAL_FINISH)
                            repeat_speed--;
                        count = repeat_speed;

                        repeat_count++;

                        /* Send a SYS_POWEROFF event if we have a device
                           which doesn't shut down easily with the OFF
                           key */
#ifdef HAVE_SW_POWEROFF
                        if ((btn == POWEROFF_BUTTON
#ifdef BUTTON_RC_STOP
                                    || btn == BUTTON_RC_STOP
#endif
                                    ) &&
#if defined(CONFIG_CHARGING) && !defined(HAVE_POWEROFF_WHILE_CHARGING)
                                !charger_inserted() &&
#endif
                                repeat_count > POWEROFF_COUNT)
                        {
                            /* Tell the main thread that it's time to
                               power off */
                            sys_poweroff();

                            /* Safety net for players without hardware
                               poweroff */
                            if(repeat_count > POWEROFF_COUNT * 10)
                                power_off();
                        }
#endif
                    }
                }
                else
                {
                    if (count++ > REPEAT_START)
                    {
                        post = true;
                        repeat = true;
                        repeat_count = 0;
                        /* initial repeat */
                        count = REPEAT_INTERVAL_START;
                    }
                }
            }
            if ( post )
            {
                if (repeat)
                {
                    /* Only post repeat events if the queue is empty,
                     * to avoid afterscroll effects. */
                    if (queue_empty(&button_queue))
                    {
                        queue_post(&button_queue, BUTTON_REPEAT | btn, NULL);
#ifdef CONFIG_BACKLIGHT
#ifdef HAVE_REMOTE_LCD
                        skip_remote_release = false;
#endif
                        skip_release = false;
#endif
                        post = false;
                    }
                }
                else
                {
#ifdef CONFIG_BACKLIGHT
#ifdef HAVE_REMOTE_LCD
                    if (btn & BUTTON_REMOTE) {
                        if (!remote_filter_first_keypress || is_remote_backlight_on()
#if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
                           || (remote_type()==REMOTETYPE_H300_NONLCD)
#endif
                            )
                            queue_post(&button_queue, btn, NULL);
                        else
                            skip_remote_release = true;
                    }
                    else
#endif
                        if (!filter_first_keypress || is_backlight_on())
                            queue_post(&button_queue, btn, NULL);
                        else
                            skip_release = true;
#else /* no backlight, nothing to skip */
                    queue_post(&button_queue, btn, NULL);
#endif
                    post = false;
                }
#ifdef HAVE_REMOTE_LCD
                if(btn & BUTTON_REMOTE)
                    remote_backlight_on();
                else
#endif
                    backlight_on();

                reset_poweroff_timer();
            }
        }
        else
        {
            repeat = false;
            count = 0;
        }
    }
    lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
}

long button_get(bool block)
{
    struct event ev;

    if ( block || !queue_empty(&button_queue) )
    {
        queue_wait(&button_queue, &ev);
        return ev.id;
    }
    return BUTTON_NONE;
}

long button_get_w_tmo(int ticks)
{
    struct event ev;
    queue_wait_w_tmo(&button_queue, &ev, ticks);
    return (ev.id != SYS_TIMEOUT)? ev.id: BUTTON_NONE;
}

void button_init(void)
{
    /* hardware inits */
#ifdef TARGET_TREE
    button_init_device();
    
#elif CONFIG_KEYPAD == IRIVER_H100_PAD
    /* Set GPIO33, GPIO37, GPIO38  and GPIO52 as general purpose inputs */
    GPIO1_FUNCTION |= 0x00100062;
    GPIO1_ENABLE &= ~0x00100060;
#elif CONFIG_KEYPAD == IRIVER_H300_PAD
    /* Set GPIO9 and GPIO15 as general purpose inputs */
    GPIO_ENABLE &= ~0x00008200;
    GPIO_FUNCTION |= 0x00008200;
    /* Set GPIO33, GPIO37, GPIO38  and GPIO52 as general purpose inputs */
    GPIO1_ENABLE &= ~0x00100060;
    GPIO1_FUNCTION |= 0x00100062;
#elif CONFIG_KEYPAD == RECORDER_PAD
    /* Set PB4 and PB8 as input pins */
    PBCR1 &= 0xfffc;  /* PB8MD = 00 */
    PBCR2 &= 0xfcff;  /* PB4MD = 00 */
    PBIOR &= ~0x0110; /* Inputs */
#elif CONFIG_KEYPAD == PLAYER_PAD
    /* set PA5 and PA11 as input pins */
    PACR1 &= 0xff3f;  /* PA11MD = 00 */
    PACR2 &= 0xfbff;  /* PA5MD = 0 */
    PAIOR &= ~0x0820; /* Inputs */
#elif CONFIG_KEYPAD == ONDIO_PAD
    /* nothing to initialize here */
#elif CONFIG_KEYPAD == GMINI100_PAD
    /* nothing to initialize here */
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) && !defined(IPOD_MINI)
    opto_i2c_init();
    /* hold button - enable as input */
    GPIOA_ENABLE |= 0x20;
    GPIOA_OUTPUT_EN &= ~0x20; 
    /* hold button - set interrupt levels */
    GPIOA_INT_LEV = ~(GPIOA_INPUT_VAL & 0x20);
    GPIOA_INT_CLR = GPIOA_INT_STAT & 0x20;
    /* enable interrupts */
    GPIOA_INT_EN = 0x20;
    /* unmask interrupt */
    CPU_INT_EN = 0x40000000;
    CPU_HI_INT_EN = I2C_MASK;

#elif (CONFIG_KEYPAD == IPOD_4G_PAD) && defined(IPOD_MINI)
    /* iPod Mini G1 */
    /* buttons - enable as input */
    GPIOA_ENABLE |= 0x3f;
    GPIOA_OUTPUT_EN &= ~0x3f;
    /* scroll wheel- enable as input */
    GPIOB_ENABLE |= 0x30; /* port b 4,5 */
    GPIOB_OUTPUT_EN &= ~0x30; /* port b 4,5 */
    /* buttons - set interrupt levels */
    GPIOA_INT_LEV = ~(GPIOA_INPUT_VAL & 0x3f);
    GPIOA_INT_CLR = GPIOA_INT_STAT & 0x3f;
    /* scroll wheel - set interrupt levels */
    GPIOB_INT_LEV = ~(GPIOB_INPUT_VAL & 0x30);
    GPIOB_INT_CLR = GPIOB_INT_STAT & 0x30;
    /* enable interrupts */
    GPIOA_INT_EN = 0x3f;
    GPIOB_INT_EN = 0x30;
    /* unmask interrupt */
    CPU_INT_EN = 0x40000000;
    CPU_HI_INT_EN = GPIO_MASK;

#elif CONFIG_KEYPAD == IPOD_3G_PAD
    GPIOA_INT_LEV = ~GPIOA_INPUT_VAL;
    GPIOA_INT_CLR = GPIOA_INT_STAT;
    GPIOA_INT_EN  = 0xff;
#endif /* CONFIG_KEYPAD */
    queue_init(&button_queue);
    button_read();
    lastbtn = button_read();
    tick_add_task(button_tick);
    reset_poweroff_timer();

#ifdef HAVE_LCD_BITMAP
    flipped = false;
#endif
#ifdef CONFIG_BACKLIGHT
    filter_first_keypress = false;
#ifdef HAVE_REMOTE_LCD
    remote_filter_first_keypress = false;
#endif    
#endif
}

#ifdef HAVE_LCD_BITMAP /* only bitmap displays can be flipped */
/*
 * helper function to swap LEFT/RIGHT, UP/DOWN (if present), and F1/F3 (Recorder)
 */
static int button_flip(int button)
{
    int newbutton;

    newbutton = button &
        ~(BUTTON_LEFT | BUTTON_RIGHT
#if defined(BUTTON_UP) && defined(BUTTON_DOWN)
        | BUTTON_UP | BUTTON_DOWN
#endif
#if CONFIG_KEYPAD == RECORDER_PAD
        | BUTTON_F1 | BUTTON_F3
#endif
        );

    if (button & BUTTON_LEFT)
        newbutton |= BUTTON_RIGHT;
    if (button & BUTTON_RIGHT)
        newbutton |= BUTTON_LEFT;
#if defined(BUTTON_UP) && defined(BUTTON_DOWN)
    if (button & BUTTON_UP)
        newbutton |= BUTTON_DOWN;
    if (button & BUTTON_DOWN)
        newbutton |= BUTTON_UP;
#endif
#if CONFIG_KEYPAD == RECORDER_PAD
    if (button & BUTTON_F1)
        newbutton |= BUTTON_F3;
    if (button & BUTTON_F3)
        newbutton |= BUTTON_F1;
#endif

    return newbutton;
}

/*
 * set the flip attribute
 * better only call this when the queue is empty
 */
void button_set_flip(bool flip)
{
    if (flip != flipped) /* not the current setting */
    {
        /* avoid race condition with the button_tick() */
        int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
        lastbtn = button_flip(lastbtn);
        flipped = flip;
        set_irq_level(oldlevel);
    }
}
#endif /* HAVE_LCD_BITMAP */

#ifdef CONFIG_BACKLIGHT
void set_backlight_filter_keypress(bool value)
{
    filter_first_keypress = value;
}
#ifdef HAVE_REMOTE_LCD
void set_remote_backlight_filter_keypress(bool value)
{
    remote_filter_first_keypress = value;
}
#endif
#endif

/*
 Archos hardware button hookup
 =============================

 Recorder / Recorder FM/V2
 -------------------------
 F1, F2, F3, UP:           connected to AN4 through a resistor network
 DOWN, PLAY, LEFT, RIGHT:  likewise connected to AN5

 The voltage on AN4/ AN5 depends on which keys (or key combo) is pressed
 FM/V2 has PLAY and RIGHT switched compared to plain recorder

 ON:     PB8, low active (plain recorder) / AN3, low active (fm/v2)
 OFF:    PB4, low active (plain recorder) / AN2, high active (fm/v2)

 Player
 ------
 LEFT:   AN0
 MENU:   AN1
 RIGHT:  AN2
 PLAY:   AN3

 STOP:   PA11
 ON:     PA5

 All buttons are low active

 Ondio
 -----
 LEFT, RIGHT, UP, DOWN:    connected to AN4 through a resistor network

 The voltage on AN4 depends on which keys (or key combo) is pressed

 OPTION: AN2, high active (assigned as MENU)
 ON/OFF: AN3, low active (assigned as OFF)

*/

#if CONFIG_KEYPAD == RECORDER_PAD

#ifdef HAVE_FMADC
/* FM Recorder super-special levels */
#define LEVEL1        150
#define LEVEL2        385
#define LEVEL3        545
#define LEVEL4        700
#define ROW2_BUTTON1  BUTTON_PLAY
#define ROW2_BUTTON3  BUTTON_RIGHT

#else
/* plain bog standard Recorder levels */
#define LEVEL1        250
#define LEVEL2        500
#define LEVEL3        700
#define LEVEL4        900
#define ROW2_BUTTON1  BUTTON_RIGHT
#define ROW2_BUTTON3  BUTTON_PLAY
#endif /* HAVE_FMADC */

#elif CONFIG_KEYPAD == ONDIO_PAD
/* Ondio levels */
#define LEVEL1        165
#define LEVEL2        415
#define LEVEL3        585
#define LEVEL4        755

#endif /* CONFIG_KEYPAD */

/*
 * Get button pressed from hardware
 */
static int button_read(void)
{
    int btn = BUTTON_NONE;
    int retval;
#ifndef TARGET_TREE
    int data;
#endif

#ifdef TARGET_TREE
    btn = button_read_device();
    
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
    static bool hold_button = false;
    static bool remote_hold_button = false;
    static int prev_data = 0xff;
    static int last_valid = 0xff;
    bool hold_button_old;

    /* normal buttons */
    hold_button_old = hold_button;
    hold_button = button_hold();

#ifndef BOOTLOADER
    if (hold_button != hold_button_old)
        backlight_hold_changed(hold_button);
#endif

    if (!hold_button)
    {
        data = adc_scan(ADC_BUTTONS);

        /* ADC debouncing: Only accept new reading if it's
         * stable (+/-1). Use latest stable value otherwise. */
        if ((unsigned)(data - prev_data + 1) <= 2)
            last_valid = data;
        prev_data = data;
        data = last_valid;

#if CONFIG_KEYPAD == IRIVER_H100_PAD
        if (data < 0xf0)
        {
            if (data < 0x80)
                if (data < 0x30)
                    if (data < 0x18)
                        btn = BUTTON_SELECT;
                    else
                        btn = BUTTON_UP;
                else
                    if (data < 0x50)
                        btn = BUTTON_LEFT;
                    else
                        btn = BUTTON_DOWN;
            else
                if (data < 0xb0)
                    if (data < 0xa0)
                        btn = BUTTON_RIGHT;
                    else
                        btn = BUTTON_OFF;
                else
                    if (data < 0xd0)
                        btn = BUTTON_MODE;
                    else
                        btn = BUTTON_REC;
        }
#else /* H300 */
        if (data < 0xba)
        {
            if (data < 0x54)
                if (data < 0x30)
                    if (data < 0x10)
                        btn = BUTTON_SELECT;
                    else
                        btn = BUTTON_UP;
                else
                    btn = BUTTON_LEFT;
            else
                if (data < 0x98)
                    if (data < 0x76)
                        btn = BUTTON_DOWN;
                    else
                        btn = BUTTON_RIGHT;
                else
                    btn = BUTTON_OFF;
        }
#endif
    }

    /* remote buttons */
    remote_hold_button = remote_button_hold_only();

    if (remote_hold_button && !remote_button_hold_only())
    {
        remote_backlight_on();
    }

    if (!remote_hold_button)
    {
        data = adc_scan(ADC_REMOTE);
        switch (remote_type())
        {
            case REMOTETYPE_H100_LCD:
                if (data < 0xf5)
                {
                    if (data < 0x73)
                        if (data < 0x3f)
                            if (data < 0x25)
                                if(data < 0x0c)
                                    btn |= BUTTON_RC_STOP;
                                else
                                    btn |= BUTTON_RC_VOL_DOWN;
                            else
                                btn |= BUTTON_RC_MODE;
                        else
                            if (data < 0x5a)
                                btn |= BUTTON_RC_VOL_UP;
                            else
                                btn |= BUTTON_RC_BITRATE;
                    else
                        if (data < 0xa8)
                            if (data < 0x8c)
                                btn |= BUTTON_RC_REC;
                            else
                                btn |= BUTTON_RC_SOURCE;
                        else
                            if (data < 0xdf)
                                if(data < 0xc5)
                                    btn |= BUTTON_RC_FF;
                                else
                                    btn |= BUTTON_RC_MENU;
                            else
                                btn |= BUTTON_RC_REW;
                }
                break;
            case REMOTETYPE_H300_LCD:
                if (data < 0xf5)
                {
                    if (data < 0x73)
                        if (data < 0x42)
                            if (data < 0x27)
                                if(data < 0x0c)
                                    btn |= BUTTON_RC_VOL_DOWN;
                                else
                                    btn |= BUTTON_RC_FF;
                            else
                                btn |= BUTTON_RC_STOP;
                        else
                            if (data < 0x5b)
                                btn |= BUTTON_RC_MODE;
                            else
                                btn |= BUTTON_RC_REC;
                    else
                        if (data < 0xab)
                            if (data < 0x8e)
                                btn |= BUTTON_RC_ON;
                            else
                                btn |= BUTTON_RC_BITRATE;
                        else
                            if (data < 0xde)
                                if(data < 0xc5)
                                    btn |= BUTTON_RC_SOURCE;
                                else
                                    btn |= BUTTON_RC_VOL_UP;
                            else
                                btn |= BUTTON_RC_REW;
                }
                break;
            case REMOTETYPE_H300_NONLCD:
                if (data < 0xf1)
                {
                    if (data < 0x7d)
                        if (data < 0x25)
                            btn |= BUTTON_RC_FF;
                        else
                            btn |= BUTTON_RC_REW;
                    else
                        if (data < 0xd5)
                            btn |= BUTTON_RC_VOL_DOWN;
                        else
                            btn |= BUTTON_RC_VOL_UP;
                }
                break;
        }
    }

    /* special buttons */
#if CONFIG_KEYPAD == IRIVER_H300_PAD
    if (!hold_button)
    {
        data = GPIO_READ;
        if ((data & 0x0200) == 0)
            btn |= BUTTON_MODE;
        if ((data & 0x8000) == 0)
            btn |= BUTTON_REC;
    }
#endif

    data = GPIO1_READ;
    if (!hold_button && ((data & 0x20) == 0))
        btn |= BUTTON_ON;
    if (!remote_hold_button && ((data & 0x40) == 0))
        switch(remote_type())
        {
            case REMOTETYPE_H100_LCD:
            case REMOTETYPE_H300_NONLCD:
                btn |= BUTTON_RC_ON;
                break;
            case REMOTETYPE_H300_LCD:
                btn |= BUTTON_RC_MENU;
                break;
        }
   
#elif CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
    static bool hold_button = false;
    bool hold_button_old;

    /* normal buttons */
    hold_button_old = hold_button;
    hold_button = button_hold();

    if (hold_button != hold_button_old)
        backlight_hold_changed(hold_button);

    if (!button_hold())
    {          
        data = adc_read(ADC_BUTTONS);
        if (data < 0x35c)
        {
            if (data < 0x151)
                if (data < 0xc7)
                    if (data < 0x41)
                        btn = BUTTON_LEFT;
                    else
                        btn = BUTTON_RIGHT;
                else
                    btn = BUTTON_SELECT;
            else
                if (data < 0x268)
                    if (data < 0x1d7)
                        btn = BUTTON_UP;
                    else
                        btn = BUTTON_DOWN;
                else
                    if (data < 0x2f9)
                        btn = BUTTON_EQ;
                    else
                        btn = BUTTON_MODE;
        }

        if (adc_read(ADC_BUTTON_PLAY) < 0x64)
            btn |= BUTTON_PLAY;
    }

#elif CONFIG_KEYPAD == RECORDER_PAD
#ifndef HAVE_FMADC
    static int off_button_count = 0;
#endif

    /* check F1..F3 and UP */
    data = adc_read(ADC_BUTTON_ROW1);
    if (data >= LEVEL1)
    {
        if (data >= LEVEL3)
            if (data >= LEVEL4)
                btn = BUTTON_F3;
            else
                btn = BUTTON_UP;
        else
            if (data >= LEVEL2)
                btn = BUTTON_F2;
            else
                btn = BUTTON_F1;
    }

    /* Some units have mushy keypads, so pressing UP also activates
       the Left/Right buttons. Let's combat that by skipping the AN5
       checks when UP is pressed. */
    if(!(btn & BUTTON_UP))
    {
        /* check DOWN, PLAY, LEFT, RIGHT */
        data = adc_read(ADC_BUTTON_ROW2);
        if (data >= LEVEL1)
        {
            if (data >= LEVEL3)
                if (data >= LEVEL4)
                    btn |= BUTTON_DOWN;
                else
                    btn |= ROW2_BUTTON3;
            else
                if (data >= LEVEL2)
                    btn |= BUTTON_LEFT;
                else
                    btn |= ROW2_BUTTON1;
        }
    }

#ifdef HAVE_FMADC
    if ( adc_read(ADC_BUTTON_ON) < 512 )
        btn |= BUTTON_ON;
    if ( adc_read(ADC_BUTTON_OFF) > 512 )
        btn |= BUTTON_OFF;
#else
    /* check port B pins for ON and OFF */
    data = PBDR;
    if ((data & 0x0100) == 0)
        btn |= BUTTON_ON;

    if ((data & 0x0010) == 0)
    {
        /* When the batteries are low, the low-battery shutdown logic causes
         * spurious OFF events due to voltage fluctuation on some units.
         * Only accept OFF when read several times in sequence. */
        if (++off_button_count > 3)
            btn |= BUTTON_OFF;
    }
    else
        off_button_count = 0;
#endif

#elif CONFIG_KEYPAD == PLAYER_PAD
    /* buttons are active low */
    if (adc_read(0) < 0x180)
        btn = BUTTON_LEFT;
    if (adc_read(1) < 0x180)
        btn |= BUTTON_MENU;
    if(adc_read(2) < 0x180)
        btn |= BUTTON_RIGHT;
    if(adc_read(3) < 0x180)
        btn |= BUTTON_PLAY;

    /* check port A pins for ON and STOP */
    data = PADR;
    if ( !(data & 0x0020) )
        btn |= BUTTON_ON;
    if ( !(data & 0x0800) )
        btn |= BUTTON_STOP;

#elif CONFIG_KEYPAD == ONDIO_PAD
    /* Check the 4 direction keys */
    data = adc_read(ADC_BUTTON_ROW1);
    if (data >= LEVEL1)
    {
        if (data >= LEVEL3)
            if (data >= LEVEL4)
                btn = BUTTON_LEFT;
            else
                btn = BUTTON_RIGHT;
        else
            if (data >= LEVEL2)
                btn = BUTTON_UP;
            else
                btn = BUTTON_DOWN;
    }

    if(adc_read(ADC_BUTTON_OPTION) > 0x200) /* active high */
        btn |= BUTTON_MENU;
    if(adc_read(ADC_BUTTON_ONOFF) < 0x120) /* active low */
        btn |= BUTTON_OFF;

#elif CONFIG_KEYPAD == GMINI100_PAD
    data = adc_read(7);
    if (data < 0x38a)
    {
        if (data < 0x1c5)
            if (data < 0xe3)
                btn = BUTTON_LEFT;
            else
                btn = BUTTON_DOWN;
        else
            if (data < 0x2a2)
                btn = BUTTON_RIGHT;
            else
                btn = BUTTON_UP;
    }

    data = adc_read(6);
    if (data < 0x355)
    {
        if (data < 0x288)
            if (data < 0x233)
                btn |= BUTTON_OFF;
            else
                btn |= BUTTON_PLAY;
        else
            btn |= BUTTON_MENU;
    }

    data = P7;
    if (data & 0x01)
        btn |= BUTTON_ON;

#elif (CONFIG_KEYPAD == IPOD_4G_PAD)
    static bool hold_button = false;
    bool hold_button_old;

    /* normal buttons */
    hold_button_old = hold_button;
    hold_button = button_hold();

    if (hold_button != hold_button_old)
        backlight_hold_changed(hold_button);

    (void)data;
    /* The int_btn variable is set in the button interrupt handler */
    btn = int_btn;

#elif (CONFIG_KEYPAD == IPOD_3G_PAD)
    static bool hold_button = false;
    bool hold_button_old;

    /* normal buttons */
    hold_button_old = hold_button;
    hold_button = button_hold();

    if (hold_button != hold_button_old)
        backlight_hold_changed(hold_button);

    (void)data;
    btn = ipod_3g_button_read();

#endif /* CONFIG_KEYPAD */

#ifdef HAVE_LCD_BITMAP
    if (btn && flipped)
        btn = button_flip(btn); /* swap upside down */
#endif

    /* Filter the button status. It is only accepted if we get the same
       status twice in a row. */
    if (btn != last_read)
        retval = lastbtn;
    else
        retval = btn;
    last_read = btn;

    return retval;
}

#if (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD)
bool button_hold(void)
{
    return (GPIOA_INPUT_VAL & 0x20)?false:true;
}
#endif

#if (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
bool button_hold(void)
{
    return (GPIO1_READ & 0x00000002)?true:false;
}

static bool remote_button_hold_only(void)
{        
    if(remote_type() == REMOTETYPE_H300_NONLCD)
        return adc_scan(ADC_REMOTE)<0x0d; /* hold should be 0x00 */
    else
        return (GPIO1_READ & 0x00100000)?true:false;
}

/* returns true only if there is remote present */
bool remote_button_hold(void)
{
    /* H300's NON-LCD remote doesn't set the "remote present" bit */
    if(remote_type() == REMOTETYPE_H300_NONLCD)
        return remote_button_hold_only();
    else
        return ((GPIO_READ & 0x40000000) == 0)?remote_button_hold_only():false;
}
#endif

#if CONFIG_KEYPAD == IRIVER_IFP7XX_PAD
bool button_hold(void)
{
    return (GPIO5_READ & 4) ? false : true;
}
#endif

int button_status(void)
{
    return lastbtn;
}

void button_clear_queue(void)
{
    queue_clear(&button_queue);
}

