2 * headphoned for the Nokia N900
4 * The headphone daemon watches the state of the headphone
5 * plug (connected, disconnected) and carries out actions
6 * based on these events.
9 * Faheem Pervez - D-Bus/HAL-based disconnect detection
11 * Initial working version: 2009-10-21
13 * Copyright (c) 2009-2010 Thomas Perl <thpinfo.com>
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License
26 * along with this package; if not, write to the Free Software
27 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 #include <dbus/dbus.h>
41 #ifdef HEADPHONED_DEBUG
42 # define debug_msg(...) { \
43 fprintf(stderr, "DEBUG: "); \
44 fprintf(stderr, __VA_ARGS__); \
48 # define debug_msg(...)
51 #define warning_msg(...) { \
52 fprintf(stderr, "WARNING: "); \
53 fprintf(stderr, __VA_ARGS__); \
54 fputc('\n', stderr); \
57 /* State file for wired headsets without microphone */
58 #define STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state"
59 #define STATE_CONNECTED_STR "connected"
60 #define STATE_DISCONNECTED_STR "disconnected"
62 /* Where the HAL Device Manager sits on the D-Bus */
63 #define HAL_MANAGER_PATH "/org/freedesktop/Hal/Manager"
64 #define HAL_MANAGER_INTF "org.freedesktop.Hal.Manager"
66 /* The signal to watch when headphones are removed */
67 #define HAL_MANAGER_SIGN "DeviceRemoved"
69 /* A D-Bus rule for filtering events we're interested in */
70 #define DBUS_RULE "type='signal',interface='" HAL_MANAGER_INTF \
71 "',path='" HAL_MANAGER_PATH \
72 "',member='" HAL_MANAGER_SIGN "'"
74 /* The name for headphones (w/ microphone?) on the D-Bus */
75 #define HEADPHONE_UDI_NAME "/org/freedesktop/Hal/devices/computer_logicaldev_input_1"
77 /* Where the Media Player backend sits on the D-Bus */
79 # define MEDIA_SERVER_SRVC "com.nokia.osso_media_server"
80 # define MEDIA_SERVER_PATH "/com/nokia/osso_media_server"
81 # define MEDIA_SERVER_INTF "com.nokia.osso_media_server.music"
83 # define MEDIA_SERVER_SRVC "com.nokia.mafw.renderer.Mafw-Gst-Renderer-Plugin.gstrenderer"
84 # define MEDIA_SERVER_PATH "/com/nokia/mafw/renderer/gstrenderer"
85 # define MEDIA_SERVER_INTF "com.nokia.mafw.renderer"
88 /* Where Panucci sits on the D-Bus */
89 #define PANUCCI_SRVC "org.panucci.panucciInterface"
90 #define PANUCCI_PATH "/panucciInterface"
91 #define PANUCCI_INTF "org.panucci.panucciInterface"
93 /* MPlayer is not yet 'on the bus', so we use a good old named pipe */
94 #define MPLAYER_FIFO "/etc/headphoned/mplayer-input"
98 DBusConnection* session_bus;
99 DBusConnection* system_bus;
100 osso_context_t* osso;
104 /* This has to be globally accessible (for signal handling below) */
105 static GMainLoop* loop = NULL;
108 sig_handler(int sig G_GNUC_UNUSED)
110 debug_msg("Received signal: %d", sig);
112 if (loop != NULL && g_main_loop_is_running(loop)) {
113 g_main_loop_quit(loop);
120 Headphoned* this = g_new0(Headphoned, 1);
122 this->osso = osso_initialize("headphoned", "1.0", FALSE, NULL);
123 assert(this->osso != NULL);
125 this->session_bus = (DBusConnection*)osso_get_dbus_connection(this->osso);
126 this->system_bus = (DBusConnection*)osso_get_sys_dbus_connection(this->osso);
128 this->initial = TRUE;
134 broadcast_pause_signal(Headphoned* headphoned)
138 debug_msg("Sending pause signal to Media Player");
139 /* Nokia Media Player */
140 osso_rpc_run(headphoned->osso,
149 if (dbus_bus_name_has_owner(headphoned->session_bus, PANUCCI_SRVC, NULL)) {
150 debug_msg("Sending pause signal to Panucci");
151 osso_rpc_run(headphoned->osso,
159 debug_msg("Panucci not running - not sending pause signal.");
163 if ((mplayer_fifo = open(MPLAYER_FIFO, O_WRONLY | O_NONBLOCK)) != -1) {
164 debug_msg("Sending pause signal to MPlayer");
165 write(mplayer_fifo, "pause\n", 6);
168 debug_msg("MPlayer not running - not sending pause signal.");
172 /* Handler for messages from "wired" headphones (via sysfs) */
174 on_file_changed(GIOChannel* source, GIOCondition condition, gpointer data)
176 Headphoned* headphoned = (Headphoned*)data;
179 debug_msg("File %s has changed.", STATE_FILE);
181 g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL);
182 g_io_channel_read_line(source, &result, NULL, NULL, NULL);
185 if (g_ascii_strcasecmp(result, STATE_DISCONNECTED_STR) == 0) {
186 if (headphoned->initial == TRUE) {
187 debug_msg("Ignoring initial file change.");
188 headphoned->initial = FALSE;
190 debug_msg("Broadcasting pause signal (cause: state file)");
191 broadcast_pause_signal(headphoned);
199 /* Handler for messages from Bluetooth + "wired w/ mic" headphones */
200 static DBusHandlerResult
201 on_msg_recieved(DBusConnection* connection G_GNUC_UNUSED, DBusMessage* message, void* data)
203 Headphoned* headphoned = (Headphoned*)data;
204 DBusMessageIter iter;
205 const char* result = NULL;
207 dbus_message_iter_init(message, &iter);
208 dbus_message_iter_get_basic(&iter, &result);
210 if (g_str_equal(result, HEADPHONE_UDI_NAME)) {
211 debug_msg("Broadcasting pause signal (cause: Hal via D-Bus)");
212 broadcast_pause_signal(headphoned);
213 return DBUS_HANDLER_RESULT_HANDLED;
215 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
220 main(int argc, char* argv[])
222 Headphoned *headphoned = NULL;
223 GIOChannel* state = NULL;
225 signal(SIGINT, sig_handler);
226 signal(SIGQUIT, sig_handler);
227 signal(SIGTERM, sig_handler);
229 loop = g_main_loop_new(NULL, FALSE);
230 headphoned = headphoned_new();
232 state = g_io_channel_new_file(STATE_FILE, "r", NULL);
234 debug_msg("Adding I/O watch on %s", STATE_FILE);
235 g_io_add_watch(state, G_IO_PRI, on_file_changed, headphoned);
237 warning_msg("Cannot open state file: %s", STATE_FILE);
240 debug_msg("Registering D-Bus rule: %s", DBUS_RULE);
241 dbus_bus_add_match(headphoned->system_bus, DBUS_RULE, NULL);
242 dbus_connection_add_filter(headphoned->system_bus, on_msg_recieved, headphoned, NULL);
244 debug_msg("Entering GLib main loop...");
245 g_main_loop_run(loop);
246 debug_msg("...main loop finished. Cleaning up.");
249 g_io_channel_unref(state);
252 osso_deinitialize(headphoned->osso);