rockbox/firmware/drivers/button.c
Miika Pekkarinen a85044bf9e New scheduler, with priorities for swcodec platforms. Frequent task
switching should be more efficient and tasks are stored in linked
lists to eliminate unnecessary task switching to improve performance.
Audio should no longer skip on swcodec targets caused by too CPU
hungry UI thread or background threads.


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@10958 a1c6a512-1295-4272-9138-f99709370657
2006-09-16 16:18:11 +00:00

1320 lines
36 KiB
C

/***************************************************************************
* __________ __ ___.
* 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
#elif defined(RC_POWEROFF_BUTTON)
|| btn == RC_POWEROFF_BUTTON
#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, true);
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;
bool remote_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_old = remote_hold_button;
remote_hold_button = remote_button_hold_only();
#ifndef BOOTLOADER
if (remote_hold_button != remote_hold_button_old)
remote_backlight_hold_changed(remote_hold_button);
#endif
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);
}