| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (C) 2002 by Daniel Stenberg |
| * |
| * 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. |
| * |
| ****************************************************************************/ |
| /* |
| * Archos Jukebox Recorder button functions |
| */ |
| |
| #include <stdlib.h> |
| #include "config.h" |
| #include "sh7034.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" |
| |
| struct event_queue button_queue; |
| |
| static int lastbtn; /* Last valid button status */ |
| #if (CONFIG_KEYPAD == RECORDER_PAD) || (CONFIG_KEYPAD == ONDIO_PAD) |
| static int last_read; /* Last button status, for debouncing/filtering */ |
| static bool flipped; /* bottons can be flipped to match the LCD flip */ |
| #endif |
| |
| /* how often we check to see if a button is pressed */ |
| #define POLL_FREQUENCY HZ/100 |
| |
| /* how long until repeat kicks in */ |
| #define REPEAT_START 30 |
| |
| /* the speed repeat starts at */ |
| #define REPEAT_INTERVAL_START 16 |
| |
| /* speed repeat finishes at */ |
| #define REPEAT_INTERVAL_FINISH 5 |
| |
| /* Number of repeated keys before shutting off */ |
| #define POWEROFF_COUNT 10 |
| |
| static int button_read(void); |
| |
| static void button_tick(void) |
| { |
| static int tick = 0; |
| static int count = 0; |
| static int repeat_speed = REPEAT_INTERVAL_START; |
| static int repeat_count = 0; |
| static bool repeat = false; |
| int diff; |
| int btn; |
| |
| #ifndef HAVE_MMC |
| /* Post events for the remote control */ |
| btn = remote_control_rx(); |
| if(btn) |
| { |
| queue_post(&button_queue, btn, NULL); |
| } |
| #endif |
| |
| /* only poll every X ticks */ |
| if ( ++tick >= POLL_FREQUENCY ) |
| { |
| bool post = false; |
| btn = button_read(); |
| |
| /* Find out if a key has been released */ |
| diff = btn ^ lastbtn; |
| if(diff && (btn & diff) == 0) |
| { |
| queue_post(&button_queue, BUTTON_REL | diff, NULL); |
| } |
| else |
| { |
| if ( btn ) |
| { |
| /* normal keypress */ |
| if ( btn != lastbtn ) |
| { |
| post = true; |
| repeat = false; |
| repeat_speed = REPEAT_INTERVAL_START; |
| |
| } |
| else /* repeat? */ |
| { |
| if ( repeat ) |
| { |
| count--; |
| if (count == 0) { |
| post = true; |
| /* yes we have repeat */ |
| repeat_speed--; |
| if (repeat_speed < REPEAT_INTERVAL_FINISH) |
| repeat_speed = REPEAT_INTERVAL_FINISH; |
| 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 == BUTTON_OFF && !charger_inserted() && |
| repeat_count > POWEROFF_COUNT) |
| queue_post(&button_queue, SYS_POWEROFF, NULL); |
| #endif |
| } |
| } |
| else |
| { |
| if (count++ > REPEAT_START) |
| { |
| post = true; |
| repeat = true; |
| repeat_count = 0; |
| /* initial repeat */ |
| count = REPEAT_INTERVAL_START; |
| } |
| } |
| } |
| if ( post ) |
| { |
| if(repeat) |
| queue_post(&button_queue, BUTTON_REPEAT | btn, NULL); |
| else |
| queue_post(&button_queue, btn, NULL); |
| backlight_on(); |
| |
| reset_poweroff_timer(); |
| } |
| } |
| else |
| { |
| repeat = false; |
| count = 0; |
| } |
| } |
| lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT); |
| tick = 0; |
| } |
| |
| backlight_tick(); |
| } |
| |
| int button_get(bool block) |
| { |
| struct event ev; |
| |
| if ( block || !queue_empty(&button_queue) ) { |
| queue_wait(&button_queue, &ev); |
| return ev.id; |
| } |
| return BUTTON_NONE; |
| } |
| |
| int 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; |
| } |
| |
| #if CONFIG_KEYPAD == RECORDER_PAD |
| |
| /* AJBR buttons are connected to the CPU as follows: |
| * |
| * ON and OFF are connected to separate port B input pins. |
| * |
| * F1, F2, F3, and UP are connected to the AN4 analog input, each through |
| * a separate voltage divider. The voltage on AN4 depends on which button |
| * (or none, or a combination) is pressed. |
| * |
| * DOWN, PLAY, LEFT, and RIGHT are likewise connected to AN5. */ |
| |
| /* Button analog voltage levels */ |
| #ifdef HAVE_FMADC |
| /* FM Recorder super-special levels */ |
| #define LEVEL1 150 |
| #define LEVEL2 385 |
| #define LEVEL3 545 |
| #define LEVEL4 700 |
| #else |
| /* plain bog standard Recorder levels */ |
| #define LEVEL1 250 |
| #define LEVEL2 500 |
| #define LEVEL3 700 |
| #define LEVEL4 900 |
| #endif |
| |
| /* |
| *Initialize buttons |
| */ |
| void button_init() |
| { |
| #ifndef SIMULATOR |
| /* Set PB4 and PB8 as input pins */ |
| PBCR1 &= 0xfffc; /* PB8MD = 00 */ |
| PBCR2 &= 0xfcff; /* PB4MD = 00 */ |
| PBIOR &= ~(PBDR_BTN_ON|PBDR_BTN_OFF); /* Inputs */ |
| #endif |
| queue_init(&button_queue); |
| lastbtn = 0; |
| tick_add_task(button_tick); |
| reset_poweroff_timer(); |
| flipped = false; |
| } |
| |
| |
| /* |
| * helper function to swap UP/DOWN, LEFT/RIGHT, F1/F3 |
| */ |
| static int button_flip(int button) |
| { |
| int newbutton; |
| |
| newbutton = button & |
| ~(BUTTON_UP | BUTTON_DOWN |
| | BUTTON_LEFT | BUTTON_RIGHT |
| | BUTTON_F1 | BUTTON_F3); |
| |
| if (button & BUTTON_UP) |
| newbutton |= BUTTON_DOWN; |
| if (button & BUTTON_DOWN) |
| newbutton |= BUTTON_UP; |
| if (button & BUTTON_LEFT) |
| newbutton |= BUTTON_RIGHT; |
| if (button & BUTTON_RIGHT) |
| newbutton |= BUTTON_LEFT; |
| if (button & BUTTON_F1) |
| newbutton |= BUTTON_F3; |
| if (button & BUTTON_F3) |
| newbutton |= BUTTON_F1; |
| |
| 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); |
| } |
| } |
| |
| /* |
| * Get button pressed from hardware |
| */ |
| static int button_read(void) |
| { |
| int btn = BUTTON_NONE; |
| int retval; |
| |
| /* Check port B pins for ON and OFF */ |
| int data; |
| |
| #ifdef HAVE_FMADC |
| /* TODO: use proper defines here, and not the numerics in the |
| function argument */ |
| if ( adc_read(3) < 512 ) |
| btn |= BUTTON_ON; |
| if ( adc_read(2) > 512 ) |
| btn |= BUTTON_OFF; |
| #else |
| data = PBDR; |
| if ((data & PBDR_BTN_ON) == 0) |
| btn |= BUTTON_ON; |
| else if ((data & PBDR_BTN_OFF) == 0) |
| btn |= BUTTON_OFF; |
| #endif |
| |
| /* Check F1-3 and UP */ |
| data = adc_read(ADC_BUTTON_ROW1); |
| if (data >= LEVEL4) |
| btn |= BUTTON_F3; |
| else if (data >= LEVEL3) |
| btn |= BUTTON_UP; |
| else if (data >= LEVEL2) |
| btn |= BUTTON_F2; |
| else if (data >= LEVEL1) |
| 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 >= LEVEL4) |
| btn |= BUTTON_DOWN; |
| else if (data >= LEVEL3) { |
| #ifdef HAVE_FMADC |
| btn |= BUTTON_RIGHT; |
| #else |
| btn |= BUTTON_PLAY; |
| #endif |
| } |
| else if (data >= LEVEL2) |
| btn |= BUTTON_LEFT; |
| else if (data >= LEVEL1) { |
| #ifdef HAVE_FMADC |
| btn |= BUTTON_PLAY; |
| #else |
| btn |= BUTTON_RIGHT; |
| #endif |
| } |
| } |
| |
| if (btn && flipped) |
| btn = button_flip(btn); /* swap upside down */ |
| |
| /* 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; |
| } |
| |
| #elif CONFIG_KEYPAD == PLAYER_PAD |
| |
| /* The player has two buttons on port pins: |
| |
| STOP: PA11 |
| ON: PA5 |
| |
| The rest are on analog inputs: |
| |
| LEFT: AN0 |
| MENU: AN1 |
| RIGHT: AN2 |
| PLAY: AN3 |
| */ |
| |
| void button_init(void) |
| { |
| /* set port pins as input */ |
| PAIOR &= ~0x820; |
| queue_init(&button_queue); |
| lastbtn = 0; |
| tick_add_task(button_tick); |
| |
| reset_poweroff_timer(); |
| } |
| |
| static int button_read(void) |
| { |
| int porta = PADR; |
| int btn = BUTTON_NONE; |
| |
| /* 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; |
| |
| if ( !(porta & 0x20) ) |
| btn |= BUTTON_ON; |
| if ( !(porta & 0x800) ) |
| btn |= BUTTON_STOP; |
| |
| return btn; |
| } |
| |
| #elif CONFIG_KEYPAD == ONDIO_PAD |
| |
| /* |
| * helper function to swap UP/DOWN, LEFT/RIGHT |
| */ |
| static int button_flip(int button) |
| { |
| int newbutton; |
| |
| newbutton = button & |
| ~(BUTTON_UP | BUTTON_DOWN |
| | BUTTON_LEFT | BUTTON_RIGHT); |
| |
| if (button & BUTTON_UP) |
| newbutton |= BUTTON_DOWN; |
| if (button & BUTTON_DOWN) |
| newbutton |= BUTTON_UP; |
| if (button & BUTTON_LEFT) |
| newbutton |= BUTTON_RIGHT; |
| if (button & BUTTON_RIGHT) |
| newbutton |= BUTTON_LEFT; |
| |
| 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); |
| } |
| } |
| |
| |
| /* The Ondio its 6 buttons on analog inputs: |
| OPTION: AN2 (used as MENU for now) |
| ON/OFF: AN3 |
| LEFT/RIGHT/UP/DOWN: AN4 |
| We map them like the player keys for now, although this is far from optimal. |
| */ |
| void button_init(void) |
| { |
| queue_init(&button_queue); |
| lastbtn = 0; |
| tick_add_task(button_tick); |
| |
| reset_poweroff_timer(); |
| } |
| |
| static int button_read(void) |
| { |
| int btn = BUTTON_NONE; |
| int retval; |
| |
| int data = adc_read(ADC_BUTTON_ROW1); |
| |
| if(adc_read(ADC_BUTTON_OPTION) > 0x200) /* active high */ |
| btn |= BUTTON_MENU; |
| if(adc_read(ADC_BUTTON_ONOFF) < 0x120) /* active low */ |
| btn |= BUTTON_OFF; |
| |
| /* Check the 4 direction keys, hard-coded analog limits for now */ |
| if (data >= 0x2EF) |
| btn |= BUTTON_LEFT; |
| else if (data >= 0x246) |
| btn |= BUTTON_RIGHT; |
| else if (data >= 0x19D) |
| btn |= BUTTON_UP; |
| else if (data >= 0x0A1) |
| btn |= BUTTON_DOWN; |
| |
| if (btn && flipped) |
| btn = button_flip(btn); /* swap upside down */ |
| |
| /* 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; |
| } |
| |
| #endif |
| |
| int button_status(void) |
| { |
| return button_read(); |
| } |
| |
| void button_clear_queue(void) |
| { |
| queue_clear(&button_queue); |
| } |