Fix non-hold button and headphone detection target sims.
[kugel-rb.git] / firmware / drivers / button.c
blob806ae6dcfa1307df49d5d4c1fa1a1e98c273a3df
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 by Daniel Stenberg
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 * Rockbox button functions
26 #include <stdlib.h>
27 #include "config.h"
28 #include "system.h"
29 #include "button.h"
30 #include "kernel.h"
31 #include "thread.h"
32 #include "backlight.h"
33 #include "serial.h"
34 #include "power.h"
35 #include "powermgmt.h"
36 #ifdef SIMULATOR
37 #include "button-sdl.h"
38 #else
39 #include "button-target.h"
40 #endif
42 #ifdef HAVE_REMOTE_LCD
43 #include "lcd-remote.h"
44 #endif
46 #if 0
47 /* Older than MAX_EVENT_AGE button events are going to be ignored.
48 * Used to prevent for example volume going up uncontrollable when events
49 * are getting queued and UI is lagging too much.
51 #define MAX_EVENT_AGE HZ
52 #endif
54 struct event_queue button_queue;
56 static long lastbtn; /* Last valid button status */
57 static long last_read; /* Last button status, for debouncing/filtering */
58 static intptr_t button_data; /* data value from last message dequeued */
59 #ifdef HAVE_LCD_BITMAP
60 static bool flipped; /* buttons can be flipped to match the LCD flip */
61 #endif
62 #ifdef HAVE_BACKLIGHT
63 static bool filter_first_keypress;
64 #ifdef HAVE_REMOTE_LCD
65 static bool remote_filter_first_keypress;
66 #endif
67 #endif /* HAVE_BACKLIGHT */
68 #ifdef HAVE_HEADPHONE_DETECTION
69 static bool phones_present = false;
70 #endif
72 /* how long until repeat kicks in, in ticks */
73 #define REPEAT_START 30
75 /* the speed repeat starts at, in ticks */
76 #define REPEAT_INTERVAL_START 16
78 /* speed repeat finishes at, in ticks */
79 #define REPEAT_INTERVAL_FINISH 5
81 #ifdef HAVE_BUTTON_DATA
82 static int button_read(int *data);
83 #else
84 static int button_read(void);
85 #endif
87 #ifdef HAVE_TOUCHSCREEN
88 static int last_touchscreen_touch;
89 #endif
90 #if defined(HAVE_HEADPHONE_DETECTION)
91 static struct timeout hp_detect_timeout; /* Debouncer for headphone plug/unplug */
92 /* This callback can be used for many different functions if needed -
93 just check to which object tmo points */
94 static int btn_detect_callback(struct timeout *tmo)
96 /* Try to post only transistions */
97 const long id = tmo->data ? SYS_PHONE_PLUGGED : SYS_PHONE_UNPLUGGED;
98 queue_remove_from_head(&button_queue, id);
99 queue_post(&button_queue, id, 0);
100 return 0;
102 #endif
104 static void button_tick(void)
106 static int count = 0;
107 static int repeat_speed = REPEAT_INTERVAL_START;
108 static int repeat_count = 0;
109 static bool repeat = false;
110 static bool post = false;
111 #ifdef HAVE_BACKLIGHT
112 static bool skip_release = false;
113 #ifdef HAVE_REMOTE_LCD
114 static bool skip_remote_release = false;
115 #endif
116 #endif
117 int diff;
118 int btn;
119 #ifdef HAVE_BUTTON_DATA
120 int data = 0;
121 #else
122 const int data = 0;
123 #endif
125 #ifdef HAS_SERIAL_REMOTE
126 /* Post events for the remote control */
127 btn = remote_control_rx();
128 if(btn)
130 queue_post(&button_queue, btn, 0);
132 #endif
134 #ifdef HAVE_BUTTON_DATA
135 btn = button_read(&data);
136 #else
137 btn = button_read();
138 #endif
140 #if defined(HAVE_HEADPHONE_DETECTION)
141 if (headphones_inserted() != phones_present)
143 /* Use the autoresetting oneshot to debounce the detection signal */
144 phones_present = !phones_present;
145 timeout_register(&hp_detect_timeout, btn_detect_callback,
146 HZ, phones_present);
148 #endif
150 /* Find out if a key has been released */
151 diff = btn ^ lastbtn;
152 if(diff && (btn & diff) == 0)
154 #ifdef HAVE_BACKLIGHT
155 #ifdef HAVE_REMOTE_LCD
156 if(diff & BUTTON_REMOTE)
157 if(!skip_remote_release)
158 queue_post(&button_queue, BUTTON_REL | diff, data);
159 else
160 skip_remote_release = false;
161 else
162 #endif
163 if(!skip_release)
164 queue_post(&button_queue, BUTTON_REL | diff, data);
165 else
166 skip_release = false;
167 #else
168 queue_post(&button_queue, BUTTON_REL | diff, data);
169 #endif
171 else
173 if ( btn )
175 /* normal keypress */
176 if ( btn != lastbtn )
178 post = true;
179 repeat = false;
180 repeat_speed = REPEAT_INTERVAL_START;
182 else /* repeat? */
184 if ( repeat )
186 if (!post)
187 count--;
188 if (count == 0) {
189 post = true;
190 /* yes we have repeat */
191 if (repeat_speed > REPEAT_INTERVAL_FINISH)
192 repeat_speed--;
193 count = repeat_speed;
195 repeat_count++;
197 /* Send a SYS_POWEROFF event if we have a device
198 which doesn't shut down easily with the OFF
199 key */
200 #ifdef HAVE_SW_POWEROFF
201 if ((btn == POWEROFF_BUTTON
202 #ifdef RC_POWEROFF_BUTTON
203 || btn == RC_POWEROFF_BUTTON
204 #endif
205 ) &&
206 #if CONFIG_CHARGING && !defined(HAVE_POWEROFF_WHILE_CHARGING)
207 !charger_inserted() &&
208 #endif
209 repeat_count > POWEROFF_COUNT)
211 /* Tell the main thread that it's time to
212 power off */
213 sys_poweroff();
215 /* Safety net for players without hardware
216 poweroff */
217 #ifndef SIMULATOR
218 if(repeat_count > POWEROFF_COUNT * 10)
219 power_off();
220 #endif
222 #endif
225 else
227 if (count++ > REPEAT_START)
229 post = true;
230 repeat = true;
231 repeat_count = 0;
232 /* initial repeat */
233 count = REPEAT_INTERVAL_START;
237 if ( post )
239 if (repeat)
241 /* Only post repeat events if the queue is empty,
242 * to avoid afterscroll effects. */
243 if (queue_empty(&button_queue))
245 queue_post(&button_queue, BUTTON_REPEAT | btn, data);
246 #ifdef HAVE_BACKLIGHT
247 #ifdef HAVE_REMOTE_LCD
248 skip_remote_release = false;
249 #endif
250 skip_release = false;
251 #endif
252 post = false;
255 else
257 #ifdef HAVE_BACKLIGHT
258 #ifdef HAVE_REMOTE_LCD
259 if (btn & BUTTON_REMOTE) {
260 if (!remote_filter_first_keypress
261 || is_remote_backlight_on(false)
262 #if defined(IRIVER_H100_SERIES) || defined(IRIVER_H300_SERIES)
263 || (remote_type()==REMOTETYPE_H300_NONLCD)
264 #endif
266 queue_post(&button_queue, btn, data);
267 else
268 skip_remote_release = true;
270 else
271 #endif
272 if (!filter_first_keypress || is_backlight_on(false)
273 #if BUTTON_REMOTE
274 || (btn & BUTTON_REMOTE)
275 #endif
277 queue_post(&button_queue, btn, data);
278 else
279 skip_release = true;
280 #else /* no backlight, nothing to skip */
281 queue_post(&button_queue, btn, data);
282 #endif
283 post = false;
285 #ifdef HAVE_REMOTE_LCD
286 if(btn & BUTTON_REMOTE)
287 remote_backlight_on();
288 else
289 #endif
291 backlight_on();
292 #ifdef HAVE_BUTTON_LIGHT
293 buttonlight_on();
294 #endif
297 reset_poweroff_timer();
300 else
302 repeat = false;
303 count = 0;
306 lastbtn = btn & ~(BUTTON_REL | BUTTON_REPEAT);
309 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
310 static void button_boost(bool state)
312 static bool boosted = false;
314 if (state && !boosted)
316 cpu_boost(true);
317 boosted = true;
319 else if (!state && boosted)
321 cpu_boost(false);
322 boosted = false;
325 #endif /* HAVE_ADJUSTABLE_CPU_FREQ */
327 int button_queue_count( void )
329 return queue_count(&button_queue);
332 long button_get(bool block)
334 struct queue_event ev;
335 int pending_count = queue_count(&button_queue);
337 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
338 /* Control the CPU boost trying to keep queue empty. */
339 if (pending_count == 0)
340 button_boost(false);
341 else if (pending_count > 2)
342 button_boost(true);
343 #endif
345 if ( block || pending_count )
347 queue_wait(&button_queue, &ev);
349 #if 0
350 /* Ignore if the event was too old and for simplicity, just
351 * wait for a new button_get() request. */
352 if (current_tick - ev.tick > MAX_EVENT_AGE)
353 return BUTTON_NONE;
354 #endif
355 button_data = ev.data;
356 return ev.id;
359 return BUTTON_NONE;
362 long button_get_w_tmo(int ticks)
364 struct queue_event ev;
366 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
367 /* Be sure to keep boosted state. */
368 if (!queue_empty(&button_queue))
369 return button_get(true);
371 button_boost(false);
372 #endif
374 queue_wait_w_tmo(&button_queue, &ev, ticks);
375 if (ev.id == SYS_TIMEOUT)
376 ev.id = BUTTON_NONE;
377 else
378 button_data = ev.data;
379 return ev.id;
382 intptr_t button_get_data(void)
384 #if defined(SIMULATOR)
385 return button_get_data_sdl();
386 #else
387 return button_data;
388 #endif
391 void button_init(void)
393 /* Init used objects first */
394 queue_init(&button_queue, true);
396 #ifdef HAVE_BUTTON_DATA
397 int temp;
398 #endif
399 /* hardware inits */
400 button_init_device();
402 #ifdef HAVE_BUTTON_DATA
403 button_read(&temp);
404 lastbtn = button_read(&temp);
405 #else
406 button_read();
407 lastbtn = button_read();
408 #endif
410 reset_poweroff_timer();
412 #ifdef HAVE_LCD_BITMAP
413 flipped = false;
414 #endif
415 #ifdef HAVE_BACKLIGHT
416 filter_first_keypress = false;
417 #ifdef HAVE_REMOTE_LCD
418 remote_filter_first_keypress = false;
419 #endif
420 #endif
421 #ifdef HAVE_TOUCHSCREEN
422 last_touchscreen_touch = 0xffff;
423 #endif
424 /* Start polling last */
425 tick_add_task(button_tick);
428 #ifndef SIMULATOR
429 #ifdef BUTTON_DRIVER_CLOSE
430 void button_close(void)
432 tick_remove_task(button_tick);
434 #endif /* BUTTON_DRIVER_CLOSE */
436 #ifdef HAVE_LCD_FLIP
438 * helper function to swap LEFT/RIGHT, UP/DOWN (if present), and F1/F3 (Recorder)
440 static int button_flip(int button)
442 int newbutton;
444 newbutton = button &
445 ~(BUTTON_LEFT | BUTTON_RIGHT
446 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
447 | BUTTON_UP | BUTTON_DOWN
448 #endif
449 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
450 | BUTTON_SCROLL_BACK | BUTTON_SCROLL_FWD
451 #endif
452 #if CONFIG_KEYPAD == RECORDER_PAD
453 | BUTTON_F1 | BUTTON_F3
454 #endif
455 #if CONFIG_KEYPAD == SANSA_C200_PAD
456 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
457 #endif
458 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
459 | BUTTON_VOL_UP | BUTTON_VOL_DOWN
460 | BUTTON_NEXT | BUTTON_PREV
461 #endif
464 if (button & BUTTON_LEFT)
465 newbutton |= BUTTON_RIGHT;
466 if (button & BUTTON_RIGHT)
467 newbutton |= BUTTON_LEFT;
468 #if defined(BUTTON_UP) && defined(BUTTON_DOWN)
469 if (button & BUTTON_UP)
470 newbutton |= BUTTON_DOWN;
471 if (button & BUTTON_DOWN)
472 newbutton |= BUTTON_UP;
473 #endif
474 #if defined(BUTTON_SCROLL_BACK) && defined(BUTTON_SCROLL_FWD)
475 if (button & BUTTON_SCROLL_BACK)
476 newbutton |= BUTTON_SCROLL_FWD;
477 if (button & BUTTON_SCROLL_FWD)
478 newbutton |= BUTTON_SCROLL_BACK;
479 #endif
480 #if CONFIG_KEYPAD == RECORDER_PAD
481 if (button & BUTTON_F1)
482 newbutton |= BUTTON_F3;
483 if (button & BUTTON_F3)
484 newbutton |= BUTTON_F1;
485 #endif
486 #if CONFIG_KEYPAD == SANSA_C200_PAD
487 if (button & BUTTON_VOL_UP)
488 newbutton |= BUTTON_VOL_DOWN;
489 if (button & BUTTON_VOL_DOWN)
490 newbutton |= BUTTON_VOL_UP;
491 #endif
492 #if CONFIG_KEYPAD == PHILIPS_SA9200_PAD
493 if (button & BUTTON_VOL_UP)
494 newbutton |= BUTTON_VOL_DOWN;
495 if (button & BUTTON_VOL_DOWN)
496 newbutton |= BUTTON_VOL_UP;
497 if (button & BUTTON_NEXT)
498 newbutton |= BUTTON_PREV;
499 if (button & BUTTON_PREV)
500 newbutton |= BUTTON_NEXT;
501 #endif
503 return newbutton;
507 * set the flip attribute
508 * better only call this when the queue is empty
510 void button_set_flip(bool flip)
512 if (flip != flipped) /* not the current setting */
514 /* avoid race condition with the button_tick() */
515 int oldlevel = disable_irq_save();
516 lastbtn = button_flip(lastbtn);
517 flipped = flip;
518 restore_irq(oldlevel);
521 #endif /* HAVE_LCD_FLIP */
523 #ifdef HAVE_BACKLIGHT
524 void set_backlight_filter_keypress(bool value)
526 filter_first_keypress = value;
528 #ifdef HAVE_REMOTE_LCD
529 void set_remote_backlight_filter_keypress(bool value)
531 remote_filter_first_keypress = value;
533 #endif
534 #endif
536 #endif /* SIMULATOR */
538 * Get button pressed from hardware
540 #ifdef HAVE_BUTTON_DATA
541 static int button_read(int *data)
543 int btn = button_read_device(data);
544 #else
545 static int button_read(void)
547 int btn = button_read_device();
548 #endif
549 int retval;
551 #ifdef HAVE_LCD_FLIP
552 if (btn && flipped)
553 btn = button_flip(btn); /* swap upside down */
554 #endif /* HAVE_LCD_FLIP */
556 #ifdef HAVE_TOUCHSCREEN
557 if (btn & BUTTON_TOUCHSCREEN)
558 last_touchscreen_touch = current_tick;
559 #endif
560 /* Filter the button status. It is only accepted if we get the same
561 status twice in a row. */
562 #ifndef HAVE_TOUCHSCREEN
563 if (btn != last_read)
564 retval = lastbtn;
565 else
566 #endif
567 retval = btn;
568 last_read = btn;
570 return retval;
573 int button_status(void)
575 return lastbtn;
578 void button_clear_queue(void)
580 queue_clear(&button_queue);
583 #ifdef HAVE_TOUCHSCREEN
584 int touchscreen_last_touch(void)
586 return last_touchscreen_touch;
588 #endif
590 #ifdef HAVE_WHEEL_ACCELERATION
591 /* WHEEL_ACCEL_FACTOR = 2^16 / WHEEL_ACCEL_START */
592 #define WHEEL_ACCEL_FACTOR (1<<16)/WHEEL_ACCEL_START
594 * data:
595 * [31] Use acceleration
596 * [30:24] Message post count (skipped + 1) (1-127)
597 * [23:0] Velocity - degree/sec
599 * WHEEL_ACCEL_FACTOR:
600 * Value in degree/sec -- configurable via settings -- above which
601 * the accelerated scrolling starts. Factor is internally scaled by
602 * 1<<16 in respect to the following 32bit integer operations.
604 int button_apply_acceleration(const unsigned int data)
606 int delta = (data >> 24) & 0x7f;
608 if ((data & (1 << 31)) != 0)
610 /* read driver's velocity from data */
611 unsigned int v = data & 0xffffff;
613 /* v = 28.4 fixed point */
614 v = (WHEEL_ACCEL_FACTOR * v)>>(16-4);
616 /* Calculate real numbers item to scroll based upon acceleration
617 * setting, use correct roundoff */
618 #if (WHEEL_ACCELERATION == 1)
619 v = (v*v + (1<< 7))>> 8;
620 #elif (WHEEL_ACCELERATION == 2)
621 v = (v*v*v + (1<<11))>>12;
622 #elif (WHEEL_ACCELERATION == 3)
623 v = (v*v*v*v + (1<<15))>>16;
624 #endif
626 if (v > 1)
627 delta *= v;
630 return delta;
632 #endif /* HAVE_WHEEL_ACCELERATION */