From 5f037ac015e6d76d030a163753db5ff58cdff49b Mon Sep 17 00:00:00 2001 From: Thomas Jarosch Date: Tue, 8 Feb 2011 20:05:25 +0000 Subject: Initial maemo platform support Adds Nokia N900, N810 and N800 support. Features: - Introduce maemo specific platform defines - Play audio in silent mode - Stop playback on incoming calls - Battery level readout - Bluetooth headset support - Save CPU by disabling screen updates if the display is off or the app doesn't have input focus - N900: GStreamer audio backend Kudos to kugel for the code review. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29248 a1c6a512-1295-4272-9138-f99709370657 --- firmware/SOURCES | 11 +- firmware/common/filefuncs.c | 2 +- firmware/common/rbpaths.c | 2 +- firmware/drivers/audio/sdl.c | 6 +- firmware/export/config.h | 10 +- firmware/export/config/application.h | 13 +- firmware/export/debug.h | 2 +- firmware/include/dir_uncached.h | 2 +- firmware/include/file.h | 2 +- firmware/target/hosted/maemo/maemo-thread.c | 220 ++++++++++ firmware/target/hosted/maemo/maemo-thread.h | 36 ++ firmware/target/hosted/maemo/pcm-gstreamer.c | 459 +++++++++++++++++++++ .../target/hosted/sdl/app/button-application.c | 16 + firmware/target/hosted/sdl/button-sdl.c | 27 +- firmware/target/hosted/sdl/button-sdl.h | 2 + firmware/target/hosted/sdl/lcd-bitmap.c | 14 + firmware/target/hosted/sdl/system-sdl.c | 50 ++- 17 files changed, 861 insertions(+), 13 deletions(-) create mode 100644 firmware/target/hosted/maemo/maemo-thread.c create mode 100644 firmware/target/hosted/maemo/maemo-thread.h create mode 100644 firmware/target/hosted/maemo/pcm-gstreamer.c (limited to 'firmware') diff --git a/firmware/SOURCES b/firmware/SOURCES index ae0c8a4f20..9c9ebf53fc 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -47,6 +47,11 @@ target/hosted/sdl/app/button-application.c #endif #endif +/* Maemo specific files */ +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) +target/hosted/maemo/maemo-thread.c +#endif + /* Standard library */ #if (CONFIG_PLATFORM & PLATFORM_NATIVE) || defined(__MINGW32__) || defined(__CYGWIN__) libc/errno.c @@ -339,8 +344,12 @@ drivers/audio/cs42l55.c #elif defined(HAVE_SDL_AUDIO) drivers/audio/sdl.c #if CONFIG_CODEC == SWCODEC +#if (CONFIG_PLATFORM & PLATFORM_MAEMO5) +target/hosted/maemo/pcm-gstreamer.c +#else target/hosted/sdl/pcm-sdl.c -#endif +#endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */ +#endif /* CONFIG_CODEC == SWCODEC */ #endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) && !defined(BOOTLOADER) */ /* USB Stack */ diff --git a/firmware/common/filefuncs.c b/firmware/common/filefuncs.c index c31971779d..3811f06ec9 100644 --- a/firmware/common/filefuncs.c +++ b/firmware/common/filefuncs.c @@ -91,7 +91,7 @@ bool dir_exists(const char *path) #endif /* __PCTOOL__ */ -#if (CONFIG_PLATFORM & (PLATFORM_NATIVE|PLATFORM_SDL)) +#if (CONFIG_PLATFORM & (PLATFORM_NATIVE|PLATFORM_SDL|PLATFORM_MAEMO)) struct dirinfo dir_get_info(DIR* parent, struct dirent *entry) { (void)parent; diff --git a/firmware/common/rbpaths.c b/firmware/common/rbpaths.c index 2d3c1e6603..ddfcff47b7 100644 --- a/firmware/common/rbpaths.c +++ b/firmware/common/rbpaths.c @@ -42,7 +42,7 @@ #define opendir opendir_android #define mkdir mkdir_android #define rmdir rmdir_android -#elif (CONFIG_PLATFORM & PLATFORM_SDL) +#elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO)) #define open sim_open #define remove sim_remove #define rename sim_rename diff --git a/firmware/drivers/audio/sdl.c b/firmware/drivers/audio/sdl.c index 7d6d7452df..021a65b9f8 100644 --- a/firmware/drivers/audio/sdl.c +++ b/firmware/drivers/audio/sdl.c @@ -34,11 +34,15 @@ extern void pcm_set_mixer_volume(int); void audiohw_set_volume(int volume) { #if CONFIG_CODEC == SWCODEC +#if (CONFIG_PLATFORM & PLATFORM_MAEMO5) + pcm_set_mixer_volume(volume); +#else pcm_set_mixer_volume( SDL_MIX_MAXVOLUME * ((volume - VOLUME_MIN) / 10) / (VOLUME_RANGE / 10)); +#endif /* (CONFIG_PLATFORM & PLATFORM_MAEMO) */ #else (void)volume; -#endif +#endif /* CONFIG_CODEC == SWCODEC */ } const struct sound_settings_info audiohw_settings[] = { diff --git a/firmware/export/config.h b/firmware/export/config.h index d59b259fb9..3063e1f06d 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -83,6 +83,9 @@ #define PLATFORM_HOSTED (1<<1) #define PLATFORM_ANDROID (1<<2) #define PLATFORM_SDL (1<<3) +#define PLATFORM_MAEMO4 (1<<4) +#define PLATFORM_MAEMO5 (1<<5) +#define PLATFORM_MAEMO (PLATFORM_MAEMO4|PLATFORM_MAEMO5) /* CONFIG_KEYPAD */ #define PLAYER_PAD 1 @@ -500,7 +503,12 @@ Lyre prototype 1 */ #endif /* define for all cpus from ARM family */ -#if (CONFIG_CPU == IMX31L) +#if (CONFIG_PLATFORM & PLATFORM_MAEMO5) && defined(MAEMO_ARM_BUILD) +#define CPU_ARM +#define ARM_ARCH 7 /* ARMv7 */ + +#elif (CONFIG_CPU == IMX31L) \ + || ((CONFIG_PLATFORM & PLATFORM_MAEMO4) && defined(MAEMO_ARM_BUILD)) #define CPU_ARM #define ARM_ARCH 6 /* ARMv6 */ diff --git a/firmware/export/config/application.h b/firmware/export/config/application.h index b731f0cf76..64ebbf3685 100644 --- a/firmware/export/config/application.h +++ b/firmware/export/config/application.h @@ -6,6 +6,10 @@ /* We don't run on hardware directly */ #ifdef ANDROID #define CONFIG_PLATFORM (PLATFORM_HOSTED|PLATFORM_ANDROID) +#elif MAEMO5 +#define CONFIG_PLATFORM (PLATFORM_HOSTED|PLATFORM_MAEMO5) +#elif MAEMO4 +#define CONFIG_PLATFORM (PLATFORM_HOSTED|PLATFORM_MAEMO4) #else #define CONFIG_PLATFORM (PLATFORM_HOSTED|PLATFORM_SDL) #endif @@ -75,17 +79,20 @@ /* Define this if you do software codec */ #define CONFIG_CODEC SWCODEC +#if (CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO)) +#define HAVE_MULTIMEDIA_KEYS +#endif + #if (CONFIG_PLATFORM & PLATFORM_ANDROID) #define CONFIG_KEYPAD ANDROID_PAD -#define HAVE_MULTIMEDIA_KEYS -#elif (CONFIG_PLATFORM & PLATFORM_SDL) +#elif (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO)) #define HAVE_SCROLLWHEEL #define CONFIG_KEYPAD SDL_PAD #else #error unknown platform #endif -#if (CONFIG_PLATFORM & PLATFORM_SDL) +#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO)) /* Use SDL audio/pcm in a SDL app build */ #define HAVE_SDL #define HAVE_SDL_AUDIO diff --git a/firmware/export/debug.h b/firmware/export/debug.h index 043ec26bbb..99cdf42c05 100644 --- a/firmware/export/debug.h +++ b/firmware/export/debug.h @@ -34,7 +34,7 @@ extern void ldebugf(const char* file, int line, const char *fmt, ...) /* */ #if defined(SIMULATOR) && !defined(__PCTOOL__) \ - || ((CONFIG_PLATFORM & PLATFORM_ANDROID) && defined(DEBUG)) + || ((CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO)) && defined(DEBUG)) #define DEBUGF debugf #define LDEBUGF(...) ldebugf(__FILE__, __LINE__, __VA_ARGS__) #else diff --git a/firmware/include/dir_uncached.h b/firmware/include/dir_uncached.h index 3bae07177b..c2c7d67505 100644 --- a/firmware/include/dir_uncached.h +++ b/firmware/include/dir_uncached.h @@ -33,7 +33,7 @@ struct dirinfo { #include #include "file.h" -#if (CONFIG_PLATFORM & PLATFORM_SDL) +#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO)) # define dirent_uncached sim_dirent # define DIR_UNCACHED SIM_DIR # define opendir_uncached sim_opendir diff --git a/firmware/include/file.h b/firmware/include/file.h index ee52c3f2b7..69ed394828 100644 --- a/firmware/include/file.h +++ b/firmware/include/file.h @@ -46,7 +46,7 @@ extern int app_open(const char *name, int o, ...); extern int app_creat(const char *name, mode_t mode); extern int app_remove(const char* pathname); extern int app_rename(const char* path, const char* newname); -# if (CONFIG_PLATFORM & PLATFORM_SDL) +# if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO)) # define filesize(x) sim_filesize(x) # define fsync(x) sim_fsync(x) # define ftruncate(x,y) sim_ftruncate(x,y) diff --git a/firmware/target/hosted/maemo/maemo-thread.c b/firmware/target/hosted/maemo/maemo-thread.c new file mode 100644 index 0000000000..f655ed597e --- /dev/null +++ b/firmware/target/hosted/maemo/maemo-thread.c @@ -0,0 +1,220 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Thomas Jarosch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include +#include +#include + +#include "config.h" +#include "system.h" +#include "kernel.h" +#include "thread.h" +#include "power.h" +#include "debug.h" +#include "button.h" + +/* Battery status information */ +#define BME_UDI "/org/freedesktop/Hal/devices/bme" +#define BATTERY_PERCENTAGE "battery.charge_level.percentage" +#define BATTER_REMAINING_TIME "battery.remaining_time" + +/* Bluetooth headset support */ +#define BT_HEADSET_UDI "/org/freedesktop/Hal/devices/computer_logicaldev_input_1" + +GMainLoop *maemo_main_loop = NULL; +osso_context_t *maemo_osso_ctx = NULL; + +volatile int maemo_display_on = 1; +volatile int maemo_battery_level = 0; +volatile int maemo_remaining_time_sec = 0; + +extern void send_battery_level_event(void); +extern int last_sent_battery_level; +extern int battery_percent; + +void display_status_callback(osso_display_state_t state, gpointer data) +{ + (void)data; + + if (state == OSSO_DISPLAY_OFF) + maemo_display_on = 0; + else + maemo_display_on = 1; +} + + +void get_battery_values(LibHalContext *ctx) +{ + /* Get initial battery percentage and remaining time */ + maemo_battery_level = libhal_device_get_property_int( + ctx, BME_UDI, + BATTERY_PERCENTAGE, NULL); + + maemo_remaining_time_sec = libhal_device_get_property_int( + ctx, BME_UDI, + BATTER_REMAINING_TIME, NULL); + + DEBUGF("[maemo] Battery percentage: %d, remaining_time_sec: %d\n", maemo_battery_level, maemo_remaining_time_sec); +} + +static void on_battery_changed (LibHalContext *ctx, + const char *udi, + const char *key, + dbus_bool_t is_removed, + dbus_bool_t is_added) +{ + (void)is_removed; + (void)is_added; + + if (!g_str_equal (udi, BME_UDI)) + return; + + if (!g_str_equal (key, BATTERY_PERCENTAGE) && !g_str_equal (key, BATTER_REMAINING_TIME)) + return; + + get_battery_values(ctx); +} + +static void on_bt_button_pressed(LibHalContext *ctx, + const char *udi, + const char *condition_name, + const char *condition_detail) +{ + (void)ctx; + + if (!g_str_equal (udi, BT_HEADSET_UDI) || !g_str_equal(condition_name, "ButtonPressed")) + return; + + sim_enter_irq_handler(); + + if (g_str_equal(condition_detail, "play-cd") || g_str_equal(condition_detail, "pause-cd")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_PLAYPAUSE, 0); + else if (g_str_equal(condition_detail, "stop-cd")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_STOP, 0); + else if (g_str_equal(condition_detail, "next-song")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_NEXT, 0); + else if (g_str_equal(condition_detail, "previous-song")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_PREV, 0); + else if (g_str_equal(condition_detail, "fast-forward")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_FFWD, 0); + else if (g_str_equal(condition_detail, "rewind")) + queue_post(&button_queue, BUTTON_MULTIMEDIA_REW, 0); + + sim_exit_irq_handler(); +} + +int maemo_thread_func (void *wait_for_osso_startup) +{ + maemo_main_loop = g_main_loop_new (NULL, FALSE); + + /* Register display callback */ + maemo_osso_ctx = osso_initialize ("rockbox", "666", FALSE, NULL); + osso_hw_set_display_event_cb(maemo_osso_ctx, display_status_callback, NULL); + + /* Register battery status callback */ + LibHalContext *hal_ctx; + hal_ctx = libhal_ctx_new(); + + DBusConnection *system_bus = (DBusConnection*)osso_get_sys_dbus_connection(maemo_osso_ctx); + libhal_ctx_set_dbus_connection(hal_ctx, system_bus); + + libhal_ctx_init(hal_ctx, NULL); + libhal_ctx_set_device_property_modified (hal_ctx, on_battery_changed); + libhal_device_add_property_watch (hal_ctx, BME_UDI, NULL); + + /* Work around libhal API issue: We need to add a property watch + to get the condition change callback working */ + libhal_device_add_property_watch (hal_ctx, BT_HEADSET_UDI, NULL); + libhal_ctx_set_device_condition(hal_ctx, on_bt_button_pressed); + + get_battery_values(hal_ctx); + + /* let system_init proceed */ + SDL_SemPost((SDL_sem *)wait_for_osso_startup); + + g_main_loop_run (maemo_main_loop); + + /* Cleanup */ + osso_deinitialize (maemo_osso_ctx); + libhal_device_remove_property_watch (hal_ctx, BT_HEADSET_UDI, NULL); + libhal_device_remove_property_watch (hal_ctx, BME_UDI, NULL); + libhal_ctx_shutdown (hal_ctx, NULL); + libhal_ctx_free(hal_ctx); + + return 0; +} + +/** Rockbox battery related functions */ +void battery_status_update(void) +{ + battery_percent = maemo_battery_level; + send_battery_level_event(); +} + +/* Returns true if any power input is connected - charging-capable + * or not. */ +bool power_input_present(void) +{ + return false; +} + +unsigned battery_voltage(void) +{ + return 0; +} + +/* Returns battery level in percent */ +int battery_level(void) +{ + battery_status_update(); + return maemo_battery_level; +} + +/* Return remaining battery time in minutes */ +int battery_time(void) +{ + battery_status_update(); + return maemo_remaining_time_sec / 60; +} + +bool battery_level_safe(void) +{ + return battery_level() >= 5; +} + +/** Rockbox stubs */ +void set_poweroff_timeout(int timeout) +{ + (void)timeout; +} + +void reset_poweroff_timer(void) +{ +} + +void shutdown_hw(void) +{ +} + +void cancel_shutdown(void) +{ +} diff --git a/firmware/target/hosted/maemo/maemo-thread.h b/firmware/target/hosted/maemo/maemo-thread.h new file mode 100644 index 0000000000..a0996b4945 --- /dev/null +++ b/firmware/target/hosted/maemo/maemo-thread.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Thomas Jarosch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __MAEMO_THREAD_H__ +#define __MAEMO_THREAD_H__ + +#include +#include + +extern osso_context_t *maemo_osso_ctx; +extern GMainLoop *maemo_main_loop; + +extern volatile int maemo_display_on; +extern volatile int maemo_has_input_focus; + +int maemo_thread_func(void *unused); +void pcm_shutdown_gstreamer(void); + +#endif diff --git a/firmware/target/hosted/maemo/pcm-gstreamer.c b/firmware/target/hosted/maemo/pcm-gstreamer.c new file mode 100644 index 0000000000..e3e40f0619 --- /dev/null +++ b/firmware/target/hosted/maemo/pcm-gstreamer.c @@ -0,0 +1,459 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2010 by Thomas Jarosch + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + + +#include "autoconf.h" + +#include +#include "config.h" +#include "debug.h" +#include "sound.h" +#include "audiohw.h" +#include "system.h" +#include "settings.h" +#include "debug.h" + +#include "playback.h" +#include "kernel.h" + +#include +#include +#include +#include +#include + +/* Maemo5: N900 specific libplayback support */ +#include +#include +#include +#include "maemo-thread.h" + +#ifdef HAVE_RECORDING +#include "audiohw.h" +#ifdef HAVE_SPDIF_IN +#include "spdif.h" +#endif +#endif + +#include "pcm.h" +#include "pcm_sampr.h" + +/*#define LOGF_ENABLE*/ +#include "logf.h" + +#ifdef DEBUG +#include +extern bool debug_audio; +#endif + +#if CONFIG_CODEC == SWCODEC + +/* Declarations for libplayblack */ +pb_playback_t *playback = NULL; +void playback_state_req_handler(pb_playback_t *pb, + enum pb_state_e req_state, + pb_req_t *ext_req, + void *data); +void playback_state_req_callback(pb_playback_t *pb, + enum pb_state_e granted_state, + const char *reason, + pb_req_t *req, + void *data); +bool playback_granted = false; + +/* Gstreamer related vars */ +GstCaps *gst_audio_caps = NULL; +GstElement *gst_pipeline = NULL; +GstElement *gst_appsrc = NULL; +GstElement *gst_volume = NULL; +GstElement *gst_pulsesink = NULL; +GstBus *gst_bus = NULL; +static int bus_watch_id = 0; +GMainLoop *pcm_loop = NULL; + +static __u8* pcm_data = NULL; +static size_t pcm_data_size = 0; + +static int inside_feed_data = 0; + +void pcm_play_lock(void) +{ +} + +void pcm_play_unlock(void) +{ +} + +void pcm_dma_apply_settings(void) +{ +} + +void pcm_play_dma_start(const void *addr, size_t size) +{ + pcm_data = (__u8 *) addr; + pcm_data_size = size; + + if (playback_granted) + { + /* Start playing now */ + if (!inside_feed_data) + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING); + else + DEBUGF("ERROR: dma_start called while inside feed_data\n"); + } else + { + /* N900: Request change to playing state */ + pb_playback_req_state (playback, + PB_STATE_PLAY, + playback_state_req_callback, + NULL); + } +} + +void pcm_play_dma_stop(void) +{ + if (inside_feed_data) + g_signal_emit_by_name (gst_appsrc, "end-of-stream", NULL); + else + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL); +} + +void pcm_play_dma_pause(bool pause) +{ + if (inside_feed_data) + { + if (pause) + g_signal_emit_by_name (gst_appsrc, "end-of-stream", NULL); + else + DEBUGF("ERROR: Called dma_pause(0) while inside feed_data\n"); + } else + { + if (pause) + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL); + else + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING); + } +} + +size_t pcm_get_bytes_waiting(void) +{ + return pcm_data_size; +} + +static void feed_data(GstElement * appsrc, guint size_hint, void *unused) +{ + (void)size_hint; + (void)unused; + + /* Make sure we don't trigger a gst_element_set_state() call + from inside gstreamer's stream thread as it will deadlock */ + inside_feed_data = 1; + + pcm_play_get_more_callback((void **)&pcm_data, &pcm_data_size); + + if (pcm_data_size != 0) + { + GstBuffer *buffer = gst_buffer_new (); + GstFlowReturn ret; + + GST_BUFFER_DATA (buffer) = pcm_data; + GST_BUFFER_SIZE (buffer) = pcm_data_size; + + g_signal_emit_by_name (appsrc, "push-buffer", buffer, &ret); + gst_buffer_unref (buffer); + + if (ret != 0) + DEBUGF("push-buffer error result: %d\n", ret); + } else + { + DEBUGF("feed_data: No Data.\n"); + g_signal_emit_by_name (appsrc, "end-of-stream", NULL); + } + + inside_feed_data = 0; +} + +const void * pcm_play_dma_get_peak_buffer(int *count) +{ + uintptr_t addr = (uintptr_t)pcm_data; + *count = pcm_data_size / 4; + return (void *)((addr + 2) & ~3); +} + + +static gboolean +gst_bus_message (GstBus * bus, GstMessage * message, void *unused) +{ + (void)bus; + (void)unused; + + DEBUGF(" [gst] got BUS message %s\n", + gst_message_type_get_name (GST_MESSAGE_TYPE (message))); + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + { + GError *err; + gchar *debug; + gst_message_parse_error (message, &err, &debug); + + DEBUGF("[gst] Received error: Src: %s, msg: %s\n", GST_MESSAGE_SRC_NAME(message), err->message); + + g_error_free (err); + g_free (debug); + } + + g_main_loop_quit (pcm_loop); + break; + case GST_MESSAGE_EOS: + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL); + break; + case GST_MESSAGE_STATE_CHANGED: + { + GstState old_state, new_state; + + gst_message_parse_state_changed (message, &old_state, &new_state, NULL); + DEBUGF("[gst] Element %s changed state from %s to %s.\n", + GST_MESSAGE_SRC_NAME(message), + gst_element_state_get_name (old_state), + gst_element_state_get_name (new_state)); + break; + } + default: + break; + } + + return TRUE; +} + +void maemo_configure_appsrc(void) +{ + /* Block push-buffer until there is enough room */ + g_object_set (G_OBJECT(gst_appsrc), "block", TRUE, NULL); + + g_object_set(G_OBJECT(gst_appsrc), "format", GST_FORMAT_BYTES, NULL); + + gst_audio_caps = gst_caps_new_simple("audio/x-raw-int", "width", G_TYPE_INT, (gint)16, "depth", G_TYPE_INT, (gint)16, "channels" ,G_TYPE_INT, (gint)2, + "signed",G_TYPE_BOOLEAN,1, + "rate",G_TYPE_INT,44100,"endianness",G_TYPE_INT,(gint)1234,NULL); + + g_object_set (G_OBJECT(gst_appsrc), "caps", gst_audio_caps, NULL); + + gst_app_src_set_stream_type(GST_APP_SRC(gst_appsrc), + GST_APP_STREAM_TYPE_STREAM); + + /* configure the appsrc, we will push data into the appsrc from the + * mainloop. */ + g_signal_connect (gst_appsrc, "need-data", G_CALLBACK (feed_data), NULL); +} + +/* Init libplayback: Grant access rights to + play audio while the phone is in silent mode */ +void maemo_init_libplayback(void) +{ + DBusConnection *session_bus_raw = (DBusConnection*)osso_get_dbus_connection(maemo_osso_ctx); + + playback = pb_playback_new_2(session_bus_raw, + PB_CLASS_MEDIA, + PB_FLAG_AUDIO, + PB_STATE_STOP, + playback_state_req_handler, + NULL); + + pb_playback_set_stream(playback, "Playback Stream"); +} + +/** + * Gets called by the policy framework if an important + * event arrives: Incoming calls etc. + */ +void maemo_tell_rockbox_to_stop_audio(void) +{ + sim_enter_irq_handler(); + queue_broadcast(SYS_CALL_INCOMING, 0); + sim_exit_irq_handler(); + + osso_system_note_infoprint(maemo_osso_ctx, "Stopping rockbox playback", NULL); +} + +void playback_state_req_handler(pb_playback_t *pb, + enum pb_state_e req_state, + pb_req_t *ext_req, + void *data) +{ + (void)pb; + (void)ext_req; + (void)data; + + DEBUGF("External state change request: state: %s, data: %p\n", + pb_state_to_string(req_state), data); + + if (req_state == PB_STATE_STOP && playback_granted) + { + DEBUGF("Stopping playback, might be an incoming call\n"); + + playback_granted = false; + maemo_tell_rockbox_to_stop_audio(); + } +} + +/** + * Callback for our own state change request. + */ +void playback_state_req_callback(pb_playback_t *pb, enum pb_state_e granted_state, const char *reason, pb_req_t *req, void *data) +{ + (void)data; + (void)reason; + + DEBUGF("State request callback: granted_state: %s, reason: %s\n", + pb_state_to_string(granted_state), reason); + + /* We are allowed to play audio */ + if (granted_state == PB_STATE_PLAY) + { + DEBUGF("Playback granted. Start playing...\n"); + playback_granted = true; + if (!inside_feed_data) + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_PLAYING); + } else + { + DEBUGF("Can't start playing. Throwing away play request\n"); + + playback_granted = false; + maemo_tell_rockbox_to_stop_audio(); + } + + pb_playback_req_completed(pb, req); +} + +void pcm_play_dma_init(void) +{ + maemo_init_libplayback(); + + GMainContext *ctx = g_main_loop_get_context(maemo_main_loop); + pcm_loop = g_main_loop_new (ctx, true); + + gst_init (NULL, NULL); + + gst_pipeline = gst_pipeline_new ("rockbox"); + + gst_appsrc = gst_element_factory_make ("appsrc", NULL); + gst_volume = gst_element_factory_make ("volume", NULL); + gst_pulsesink = gst_element_factory_make ("pulsesink", NULL); + + /* Connect elements */ + gst_bin_add_many (GST_BIN (gst_pipeline), + gst_appsrc, gst_volume, gst_pulsesink, NULL); + gst_element_link_many (gst_appsrc, gst_volume, gst_pulsesink, NULL); + + /* Connect to gstreamer bus of the pipeline */ + gst_bus = gst_pipeline_get_bus (GST_PIPELINE (gst_pipeline)); + bus_watch_id = gst_bus_add_watch (gst_bus, (GstBusFunc) gst_bus_message, NULL); + + maemo_configure_appsrc(); +} + +void pcm_shutdown_gstreamer(void) +{ + /* Try to stop playing */ + gst_element_set_state (GST_ELEMENT(gst_pipeline), GST_STATE_NULL); + + /* Make sure we are really stopped. This should return almost instantly, + so we wait up to ten seconds and just continue otherwise */ + gst_element_get_state (GST_ELEMENT(gst_pipeline), NULL, NULL, GST_SECOND * 10); + + g_source_remove (bus_watch_id); + g_object_unref(gst_bus); + gst_bus = NULL; + + gst_object_unref (gst_pipeline); + gst_pipeline = NULL; + + /* Shutdown libplayback and gstreamer */ + pb_playback_destroy (playback); + gst_deinit(); + + g_main_loop_quit(pcm_loop); + g_main_loop_unref (pcm_loop); +} + +void pcm_postinit(void) +{ +} + +void pcm_set_mixer_volume(int volume) +{ + /* gstreamer volume range is from 0.00 to 1.00 */ + gdouble gst_vol = (gdouble)(volume - VOLUME_MIN) / (gdouble)VOLUME_RANGE; + + g_object_set (G_OBJECT(gst_volume), "volume", gst_vol, NULL); +} + + +#ifdef HAVE_RECORDING +void pcm_rec_lock(void) +{ +} + +void pcm_rec_unlock(void) +{ +} + +void pcm_rec_dma_init(void) +{ +} + +void pcm_rec_dma_close(void) +{ +} + +void pcm_rec_dma_start(void *start, size_t size) +{ + (void)start; + (void)size; +} + +void pcm_rec_dma_stop(void) +{ +} + +const void * pcm_rec_dma_get_peak_buffer(void) +{ + return NULL; +} + +void audiohw_set_recvol(int left, int right, int type) +{ + (void)left; + (void)right; + (void)type; +} + +#ifdef HAVE_SPDIF_IN +unsigned long spdif_measure_frequency(void) +{ + return 0; +} +#endif + +#endif /* HAVE_RECORDING */ + +#endif /* CONFIG_CODEC == SWCODEC */ diff --git a/firmware/target/hosted/sdl/app/button-application.c b/firmware/target/hosted/sdl/app/button-application.c index 2ad09dc373..72f4a1770d 100644 --- a/firmware/target/hosted/sdl/app/button-application.c +++ b/firmware/target/hosted/sdl/app/button-application.c @@ -30,6 +30,9 @@ int key_to_button(int keyboard_key) int new_btn = BUTTON_NONE; switch (keyboard_key) { +#if (CONFIG_PLATFORM & PLATFORM_MAEMO4) + case SDLK_ESCAPE: +#endif case SDLK_KP7: new_btn = BUTTON_TOPLEFT; break; @@ -37,6 +40,9 @@ int key_to_button(int keyboard_key) case SDLK_UP: new_btn = BUTTON_TOPMIDDLE; break; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO4) + case SDLK_F7: +#endif case SDLK_KP9: new_btn = BUTTON_TOPRIGHT; break; @@ -44,6 +50,10 @@ int key_to_button(int keyboard_key) case SDLK_LEFT: new_btn = BUTTON_MIDLEFT; break; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + case SDLK_RETURN: + case SDLK_KP_ENTER: +#endif case SDLK_KP5: new_btn = BUTTON_CENTER; break; @@ -51,6 +61,9 @@ int key_to_button(int keyboard_key) case SDLK_RIGHT: new_btn = BUTTON_MIDRIGHT; break; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO4) + case SDLK_F6: +#endif case SDLK_KP1: new_btn = BUTTON_BOTTOMLEFT; break; @@ -58,6 +71,9 @@ int key_to_button(int keyboard_key) case SDLK_DOWN: new_btn = BUTTON_BOTTOMMIDDLE; break; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO4) + case SDLK_F8: +#endif case SDLK_KP3: new_btn = BUTTON_BOTTOMRIGHT; break; diff --git a/firmware/target/hosted/sdl/button-sdl.c b/firmware/target/hosted/sdl/button-sdl.c index 3321a01bc6..25dad09341 100644 --- a/firmware/target/hosted/sdl/button-sdl.c +++ b/firmware/target/hosted/sdl/button-sdl.c @@ -67,6 +67,8 @@ struct event_queue button_queue; static int btn = 0; /* Hopefully keeps track of currently pressed keys... */ +int sdl_app_has_input_focus = 1; + #ifdef HAS_BUTTON_HOLD bool hold_button_state = false; bool button_hold(void) { @@ -209,11 +211,34 @@ static void mouse_event(SDL_MouseButtonEvent *event, bool button_up) static bool event_handler(SDL_Event *event) { + SDLKey ev_key; + switch(event->type) { + case SDL_ACTIVEEVENT: + if (event->active.state & SDL_APPINPUTFOCUS) + { + if (event->active.gain == 1) + sdl_app_has_input_focus = 1; + else + sdl_app_has_input_focus = 0; + } + break; case SDL_KEYDOWN: case SDL_KEYUP: - button_event(event->key.keysym.sym, event->type == SDL_KEYDOWN); + ev_key = event->key.keysym.sym; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO5) + /* N900 with shared up/down cursor mapping. Seen on the German, + Finnish, Italian, French and Russian version. Probably more. */ + if (event->key.keysym.mod & KMOD_MODE) + { + if (ev_key == SDLK_LEFT) + ev_key = SDLK_UP; + else if (ev_key == SDLK_RIGHT) + ev_key = SDLK_DOWN; + } +#endif + button_event(ev_key, event->type == SDL_KEYDOWN); break; #ifdef HAVE_TOUCHSCREEN case SDL_MOUSEMOTION: diff --git a/firmware/target/hosted/sdl/button-sdl.h b/firmware/target/hosted/sdl/button-sdl.h index e6fcb9ea83..6b7f632eaf 100644 --- a/firmware/target/hosted/sdl/button-sdl.h +++ b/firmware/target/hosted/sdl/button-sdl.h @@ -26,6 +26,8 @@ #include #include "config.h" +extern int sdl_app_has_input_focus; + bool button_hold(void); #undef button_init_device void button_init_device(void); diff --git a/firmware/target/hosted/sdl/lcd-bitmap.c b/firmware/target/hosted/sdl/lcd-bitmap.c index 7058b26e8c..4c296924af 100644 --- a/firmware/target/hosted/sdl/lcd-bitmap.c +++ b/firmware/target/hosted/sdl/lcd-bitmap.c @@ -22,8 +22,12 @@ #include "debug.h" #include "sim-ui-defines.h" #include "system.h" +#include "button-sdl.h" #include "lcd-sdl.h" #include "screendump.h" +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) +#include "maemo-thread.h" +#endif SDL_Surface* lcd_surface; @@ -123,6 +127,16 @@ void lcd_update(void) void lcd_update_rect(int x_start, int y_start, int width, int height) { +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Don't update display if not shown */ + if (!maemo_display_on) + return; + + /* Don't update if we don't have the input focus */ + if (!sdl_app_has_input_focus) + return; +#endif + sdl_update_rect(lcd_surface, x_start, y_start, width, height, LCD_WIDTH, LCD_HEIGHT, get_lcd_pixel); sdl_gui_update(lcd_surface, x_start, y_start, width, diff --git a/firmware/target/hosted/sdl/system-sdl.c b/firmware/target/hosted/sdl/system-sdl.c index b900d38b43..9ea25c1014 100644 --- a/firmware/target/hosted/sdl/system-sdl.c +++ b/firmware/target/hosted/sdl/system-sdl.c @@ -40,6 +40,13 @@ #include "panic.h" #include "debug.h" +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) +#include +#include +#include "maemo-thread.h" + +#endif + SDL_Surface *gui_surface; bool background = true; /* use backgrounds by default */ @@ -82,9 +89,13 @@ static int sdl_event_thread(void * param) { SDL_InitSubSystem(SDL_INIT_VIDEO); +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + SDL_sem *wait_for_maemo_startup; +#endif SDL_Surface *picture_surface = NULL; int width, height; int depth; + Uint32 flags; /* Try and load the background image. If it fails go without */ if (background) { @@ -121,10 +132,21 @@ static int sdl_event_thread(void * param) if (depth < 8) depth = 16; - if ((gui_surface = SDL_SetVideoMode(width * display_zoom, height * display_zoom, depth, SDL_HWSURFACE|SDL_DOUBLEBUF)) == NULL) { + flags = SDL_HWSURFACE|SDL_DOUBLEBUF; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Fullscreen mode for maemo app */ + flags |= SDL_FULLSCREEN; +#endif + + if ((gui_surface = SDL_SetVideoMode(width * display_zoom, height * display_zoom, depth, flags)) == NULL) { panicf("%s", SDL_GetError()); } +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Hide mouse cursor on real touchscreen device */ + SDL_ShowCursor(SDL_DISABLE); +#endif + SDL_WM_SetCaption(UI_TITLE, NULL); if (background && picture_surface != NULL) @@ -133,10 +155,30 @@ static int sdl_event_thread(void * param) /* let system_init proceed */ SDL_SemPost((SDL_sem *)param); +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Start maemo thread: Listen to display on/off events and battery monitoring */ + wait_for_maemo_startup = SDL_CreateSemaphore(0); /* 0-count so it blocks */ + SDL_Thread *maemo_thread = SDL_CreateThread(maemo_thread_func, wait_for_maemo_startup); +#endif + /* * finally enter the button loop */ gui_message_loop(); +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Ensure maemo thread is up and running */ + SDL_SemWait(wait_for_maemo_startup); + SDL_DestroySemaphore(wait_for_maemo_startup); + +#if (CONFIG_PLATFORM & PLATFORM_MAEMO5) + pcm_shutdown_gstreamer(); +#endif + + g_main_loop_quit (maemo_main_loop); + g_main_loop_unref(maemo_main_loop); + SDL_WaitThread(maemo_thread, NULL); +#endif + if(picture_surface) SDL_FreeSurface(picture_surface); @@ -161,6 +203,12 @@ void system_init(void) { SDL_sem *s; +#if (CONFIG_PLATFORM & PLATFORM_MAEMO) + /* Make glib thread safe */ + g_thread_init(NULL); + g_type_init(); +#endif + if (SDL_Init(SDL_INIT_TIMER)) panicf("%s", SDL_GetError()); -- cgit v1.2.3