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
diff --git a/apps/codecs.c b/apps/codecs.c
index 88478e8..646d5f2 100644
--- a/apps/codecs.c
+++ b/apps/codecs.c
@@ -54,7 +54,7 @@
 #define LOGF_ENABLE
 #include "logf.h"
 
-#if (CONFIG_PLATFORM & PLATFORM_SDL)
+#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO))
 #define PREFIX(_x_) sim_ ## _x_
 #else
 #define PREFIX(_x_) _x_
diff --git a/apps/main.c b/apps/main.c
index 6378833..3fc48be 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -125,7 +125,7 @@
 #define MAIN_NORETURN_ATTR
 #endif
 
-#if (CONFIG_PLATFORM & PLATFORM_SDL)
+#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO))
 #include "sim_tasks.h"
 #include "system-sdl.h"
 #define HAVE_ARGV_MAIN
@@ -351,7 +351,7 @@
     show_logo();
     button_init();
     backlight_init();
-#if (CONFIG_PLATFORM & PLATFORM_SDL)
+#if (CONFIG_PLATFORM & (PLATFORM_SDL|PLATFORM_MAEMO))
     sim_tasks_init();
 #endif
 #if (CONFIG_PLATFORM & PLATFORM_ANDROID)
diff --git a/apps/misc.c b/apps/misc.c
index 94b2de4..a08dac3 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -526,7 +526,7 @@
 
 long default_event_handler_ex(long event, void (*callback)(void *), void *parameter)
 {
-#if CONFIG_PLATFORM & PLATFORM_ANDROID
+#if CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO)
     static bool resume = false;
 #endif
 
@@ -619,7 +619,7 @@
             iap_handlepkt();
             return SYS_IAP_HANDLEPKT;
 #endif
-#if CONFIG_PLATFORM & PLATFORM_ANDROID
+#if CONFIG_PLATFORM & (PLATFORM_ANDROID|PLATFORM_MAEMO)
         /* stop playback if we receive a call */
         case SYS_CALL_INCOMING:
             resume = audio_status() == AUDIO_STATUS_PLAY;
diff --git a/firmware/SOURCES b/firmware/SOURCES
index ae0c8a4..9c9ebf5 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -47,6 +47,11 @@
 #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 @@
 #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 c319717..3811f06 100644
--- a/firmware/common/filefuncs.c
+++ b/firmware/common/filefuncs.c
@@ -91,7 +91,7 @@
 
 #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 2d3c1e6..ddfcff4 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 7d6d745..021a65b 100644
--- a/firmware/drivers/audio/sdl.c
+++ b/firmware/drivers/audio/sdl.c
@@ -34,11 +34,15 @@
 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 d59b259..3063e1f 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 @@
 #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 b731f0c..64ebbf3 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 043ec26..99cdf42 100644
--- a/firmware/export/debug.h
+++ b/firmware/export/debug.h
@@ -34,7 +34,7 @@
 
 /*  */
 #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 3bae071..c2c7d67 100644
--- a/firmware/include/dir_uncached.h
+++ b/firmware/include/dir_uncached.h
@@ -33,7 +33,7 @@
 #include <stdbool.h>
 #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 ee52c3f..69ed394 100644
--- a/firmware/include/file.h
+++ b/firmware/include/file.h
@@ -46,7 +46,7 @@
 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 0000000..f655ed5
--- /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 <libhal.h>
+#include <libosso.h>
+#include <SDL_thread.h>
+
+#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 0000000..a0996b4
--- /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 <glib.h>
+#include <libosso.h>
+
+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 0000000..e3e40f0
--- /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 <stdbool.h>
+#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 <SDL.h>
+#include <glib.h>
+#include <gst/gst.h>
+#include <gst/app/gstappsrc.h>
+#include <linux/types.h>
+
+/* Maemo5: N900 specific libplayback support */
+#include <libplayback/playback.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+#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 <stdio.h>
+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 2ad09dc..72f4a17 100644
--- a/firmware/target/hosted/sdl/app/button-application.c
+++ b/firmware/target/hosted/sdl/app/button-application.c
@@ -30,6 +30,9 @@
     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 @@
         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 @@
         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 @@
         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 @@
         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 3321a01..25dad09 100644
--- a/firmware/target/hosted/sdl/button-sdl.c
+++ b/firmware/target/hosted/sdl/button-sdl.c
@@ -67,6 +67,8 @@
 
 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 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 e6fcb9e..6b7f632 100644
--- a/firmware/target/hosted/sdl/button-sdl.h
+++ b/firmware/target/hosted/sdl/button-sdl.h
@@ -26,6 +26,8 @@
 #include <stdbool.h>
 #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 7058b26..4c29692 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_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 b900d38..9ea25c1 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 <glib.h>
+#include <glib-object.h>
+#include "maemo-thread.h"
+
+#endif
+
 SDL_Surface    *gui_surface;
 
 bool            background = true;          /* use backgrounds by default */
@@ -82,9 +89,13 @@
 {
     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 @@
     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 @@
     /* 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 @@
 {
     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());
 
diff --git a/tools/configure b/tools/configure
index aa37ef7..26425ff 100755
--- a/tools/configure
+++ b/tools/configure
@@ -51,7 +51,7 @@
 }
 
 app_get_platform() {
-    echo "Select your platform: (S)DL, (A)ndroid (default: Android)"
+    echo "Select your platform: (S)DL, (A)ndroid, (M)aemo (default: Android)"
     if [ -z "$ARG_PLATFORM" ]; then
         choice=`input`
     else
@@ -60,6 +60,7 @@
 
     case $choice in
         s|S*)     app_platform="sdl" ;;
+        m|M*)     app_platform="maemo" ;;
         *|a|A*)   app_platform="android" ;;
     esac
 
@@ -128,6 +129,31 @@
         libdir="/data/data/org.rockbox/app_rockbox"
         output="librockbox.so"
         bootoutput="librockbox.so"
+    elif [ "$app_platform" = "maemo" ]; then
+        if [ -z "$ARG_PREFIX" ]; then
+            # Rockbox is in /opt as there is enough free space for it on the N900.
+            sharedir="/opt/rockbox/share/rockbox"
+            bindir="/opt/rockbox/bin"
+            libdir="/opt/rockbox/lib"
+        else
+            if [ -d "$ARG_PREFIX" ]; then
+                if [ -z `echo $ARG_PREFIX | grep "^/"` ]; then
+                    ARG_PREFIX=`realpath $ARG_PREFIX`
+                    if [ "0" != "$?" ]; then
+                        echo "ERROR: Could not get prefix path (is realpath installed?)."
+                        exit
+                    fi
+                fi
+                sharedir="$ARG_PREFIX/share/rockbox"
+                bindir="$ARG_PREFIX/bin"
+                libdir="$ARG_PREFIX/lib"
+            else
+                echo "ERROR: PREFIX does not exist"
+                exit
+            fi
+        fi
+        output="rockbox"
+        bootoutput="rockbox"
     fi
 }
 
@@ -189,6 +215,9 @@
 appcc () {
     if [ "$1" = "sdl" ]; then
         simcc "sdl-app"
+    elif [ "$1" = "maemo" ]; then
+        app_type="sdl-app"
+        maemocc
     elif [ "$1" = "android" ]; then
         app_type=$1
         androidcc
@@ -477,6 +506,68 @@
  gccchoice="4.1.2"
 }
 
+maemocc () {
+ # Scratchbox sets up "gcc" based on the active target
+ prefixtools ""
+
+ GCCOPTS=`echo $CCOPTS | sed -e s/-ffreestanding// -e s/-nostdlib// -e s/-Wundef//`
+ GCCOPTS="$GCCOPTS -fno-builtin -g -I\$(SIMDIR)"
+ GCCOPTIMIZE=''
+ LDOPTS="-lm -ldl $LDOPTS"
+ GLOBAL_LDOPTS="$GLOBAL_LDOPTS -Wl,-z,defs"
+ SHARED_FLAG="-shared"
+ endian="little"
+
+ is_n900=0
+ # Determine maemo version
+ if pkg-config --atleast-version=5 maemo-version; then
+    extradefines="$extradefines -DMAEMO5"
+    echo "Found N900 maemo version"
+    is_n900=1
+ elif pkg-config --atleast-version=4 maemo-version; then
+    extradefines="$extradefines -DMAEMO4"
+    echo "Found N8xx maemo version"
+ else
+    echo "Unable to determine maemo version. Is the maemo-version-dev package installed?"
+    exit 1
+ fi
+
+ # SDL
+ GCCOPTS="$GCCOPTS `pkg-config --cflags sdl`"
+ LDOPTS="$LDOPTS `pkg-config --libs sdl`"
+
+ # glib and libosso support
+ GCCOPTS="$GCCOPTS `pkg-config --cflags libosso glib-2.0 gthread-2.0`"
+ LDOPTS="$LDOPTS `pkg-config --libs libosso glib-2.0 gthread-2.0`"
+
+ # libhal support: Battery monitoring
+ GCCOPTS="$GCCOPTS `pkg-config --cflags hal`"
+ LDOPTS="$LDOPTS `pkg-config --libs hal`"
+
+ GCCOPTS="$GCCOPTS -O2 -fno-strict-aliasing"
+ if [ $is_n900 -eq 1 ]; then
+    # gstreamer support: Audio output.
+    GCCOPTS="$GCCOPTS `pkg-config --cflags gstreamer-base-0.10 gstreamer-plugins-base-0.10 gstreamer-app-0.10`"
+    LDOPTS="$LDOPTS `pkg-config --libs gstreamer-base-0.10 gstreamer-plugins-base-0.10 gstreamer-app-0.10`"
+
+    # N900 specific: libplayback support
+    GCCOPTS="$GCCOPTS `pkg-config --cflags libplayback-1`"
+    LDOPTS="$LDOPTS `pkg-config --libs libplayback-1`"
+
+    # N900 specific: Enable ARMv7 NEON support
+    if sb-conf current |grep ARMEL; then
+        GCCOPTS="$GCCOPTS -mcpu=cortex-a8 -mtune=cortex-a8 -mfpu=neon -mfloat-abi=softfp"
+        extradefines="$extradefines -DMAEMO_ARM_BUILD"
+    fi
+ else
+    # N8xx specific: Enable armv5te instructions
+    if sb-conf current |grep ARMEL; then
+        GCCOPTS="$GCCOPTS -mcpu=arm1136jf-s -mfloat-abi=softfp -mfpu=vfp"
+        extradefines="$extradefines -DMAEMO_ARM_BUILD"
+    fi
+ fi
+}
+
 androidcc () {
     buildhost=`uname | tr [:upper:] [:lower:]`
     gccchoice="4.4.3"
@@ -3239,9 +3330,14 @@
 
 if test -n "$t_cpu"; then
   TARGET_INC="-I\$(FIRMDIR)/target/$t_cpu/$t_manufacturer/$t_model"
-  if [ "$simulator" = "yes" ]; then # a few more includes for the sim target tree
-    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/sdl/"
-    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/"
+
+  if [ "$t_cpu" = "hosted" ] && [ "$t_manufacturer" = "maemo" ]; then
+    # Maemo needs the SDL port, too
+    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/sdl/app"
+    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/sdl"
+  elif [ "$simulator" = "yes" ]; then # a few more includes for the sim target tree
+    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted/sdl"
+    TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/hosted"
   fi
   TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/$t_cpu/$t_manufacturer"
   TARGET_INC="$TARGET_INC -I\$(FIRMDIR)/target/$t_cpu"
@@ -3282,6 +3378,8 @@
   cmdline="$cmdline--platform=S "
 elif [ "$app_platform" = "android" ]; then
   cmdline="$cmdline--platform=A "
+elif [ "$app_platform" = "maemo" ]; then
+  cmdline="$cmdline--platform=M "
 fi
 if [ "$modelname" = "application" ]; then
   cmdline="$cmdline--lcdwidth=$ARG_LCDWIDTH --lcdheight=$ARG_LCDHEIGHT "
diff --git a/uisimulator/common/SOURCES b/uisimulator/common/SOURCES
index fd256c5..2c921ed 100644
--- a/uisimulator/common/SOURCES
+++ b/uisimulator/common/SOURCES
@@ -12,5 +12,7 @@
 io.c
 sim_tasks.c
 /* this is still needed for application since it has some stubs */
+#if !(CONFIG_PLATFORM & PLATFORM_MAEMO)
 powermgmt-sim.c
+#endif
 stubs.c