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>
36 #include <libplayback/playback.h>
42 #ifdef HEADPHONED_DEBUG
43 # define debug_msg(...) { \
44 fprintf(stderr, "DEBUG: "); \
45 fprintf(stderr, __VA_ARGS__); \
49 # define debug_msg(...)
52 #define warning_msg(...) { \
53 fprintf(stderr, "WARNING: "); \
54 fprintf(stderr, __VA_ARGS__); \
55 fputc('\n', stderr); \
58 /* State file for wired headsets without microphone */
59 #define STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state"
60 #define STATE_CONNECTED_STR "connected"
61 #define STATE_DISCONNECTED_STR "disconnected"
63 /* Where the HAL Device Manager sits on the D-Bus */
64 #define HAL_MANAGER_PATH "/org/freedesktop/Hal/Manager"
65 #define HAL_MANAGER_INTF "org.freedesktop.Hal.Manager"
67 /* The signal to watch when headphones are removed */
68 #define HAL_MANAGER_SIGN "DeviceRemoved"
70 /* A D-Bus rule for filtering events we're interested in */
71 #define DBUS_RULE_HAL "type='signal',interface='" HAL_MANAGER_INTF \
72 "',path='" HAL_MANAGER_PATH \
73 "',member='" HAL_MANAGER_SIGN "'"
75 /* The name for headphones (w/ microphone?) on the D-Bus */
76 #define HEADPHONE_UDI_NAME "/org/freedesktop/Hal/devices/computer_logicaldev_input_1"
78 #define MCE_SIGNAL_INTF "com.nokia.mce.signal"
79 #define MCE_SIGNAL_PATH "/com/nokia/mce/signal"
80 #define MCE_CALL_STATE_SIG "sig_call_state_ind"
82 #define MCE_CALL_ACTIVE "active"
83 #define MCE_CALL_RINGING "ringing"
84 #define MCE_CALL_DISCONNECT "none"
86 /* A D-Bus rule for getting the call state (active/none/...) */
87 #define DBUS_RULE_CALL "type='signal',interface='" MCE_SIGNAL_INTF \
88 "',path='" MCE_SIGNAL_PATH \
89 "',member='" MCE_CALL_STATE_SIG "'"
91 /* Delay in milliseconds for the pause-after-call pause signal */
92 #define POST_CALL_PAUSE_DELAY_MS 500
94 /* Where the Media Player backend sits on the D-Bus */
96 # define MEDIA_SERVER_SRVC "com.nokia.osso_media_server"
97 # define MEDIA_SERVER_PATH "/com/nokia/osso_media_server"
98 # define MEDIA_SERVER_INTF "com.nokia.osso_media_server.music"
100 # define MEDIA_SERVER_SRVC "com.nokia.mafw.renderer.Mafw-Gst-Renderer-Plugin.gstrenderer"
101 # define MEDIA_SERVER_PATH "/com/nokia/mafw/renderer/gstrenderer"
102 # define MEDIA_SERVER_INTF "com.nokia.mafw.renderer"
105 /* Where Panucci sits on the D-Bus */
106 #define PANUCCI_SRVC "org.panucci.panucciInterface"
107 #define PANUCCI_PATH "/panucciInterface"
108 #define PANUCCI_INTF "org.panucci.panucciInterface"
110 /* MPlayer is not yet 'on the bus', so we use a good old named pipe */
111 #define MPLAYER_FIFO "/etc/headphoned/mplayer-input"
113 /* Martin's FM Radio app on the D-Bus */
114 #define FMRADIO_SRVC "de.pycage.fmradio"
115 #define FMRADIO_PATH "/de/pycage/fmradio"
116 #define FMRADIO_INTF "de.pycage.fmradio"
118 /* Martin's MediaBox on the D-Bus */
119 #define MEDIABOX_SRVC "de.pycage.mediabox"
120 #define MEDIABOX_PATH "/de/pycage/mediabox/control"
121 #define MEDIABOX_INTF "de.pycage.mediabox.control"
124 DBusConnection
* session_bus
;
125 DBusConnection
* system_bus
;
126 osso_context_t
* osso
;
127 pb_playback_t
*playback
;
129 gboolean call_active
; /* True while a call is active */
130 gboolean unplugged_on_call
; /* True when unplugged during active call */
133 /* This has to be globally accessible (for signal handling below) */
134 static GMainLoop
* loop
= NULL
;
137 sig_handler(int sig G_GNUC_UNUSED
)
139 debug_msg("Received signal: %d", sig
);
141 if (loop
!= NULL
&& g_main_loop_is_running(loop
)) {
142 g_main_loop_quit(loop
);
147 libplayback_state_request_handler(pb_playback_t
*pb
,
148 enum pb_state_e req_state G_GNUC_UNUSED
,
150 void *data G_GNUC_UNUSED
)
152 pb_playback_req_completed(pb
, req
);
156 libplayback_state_reply(pb_playback_t
*pb
,
157 enum pb_state_e granted_state G_GNUC_UNUSED
,
158 const char *reason G_GNUC_UNUSED
,
160 void *data G_GNUC_UNUSED
)
162 pb_playback_req_completed(pb
, req
);
168 Headphoned
* this = g_new0(Headphoned
, 1);
170 this->osso
= osso_initialize("headphoned", "1.0", FALSE
, NULL
);
171 assert(this->osso
!= NULL
);
173 this->session_bus
= (DBusConnection
*)osso_get_dbus_connection(this->osso
);
174 this->system_bus
= (DBusConnection
*)osso_get_sys_dbus_connection(this->osso
);
176 this->playback
= pb_playback_new_2(this->session_bus
,
180 (PBStateRequest
)libplayback_state_request_handler
,
183 this->initial
= TRUE
;
184 this->call_active
= FALSE
;
185 this->unplugged_on_call
= FALSE
;
191 broadcast_pause_signal(Headphoned
* headphoned
)
195 debug_msg("Sending pause signal to Media Player");
196 /* Nokia Media Player */
197 osso_rpc_run(headphoned
->osso
,
204 pb_playback_req_state(headphoned
->playback
,
206 (PBStateReply
)libplayback_state_reply
,
210 if (dbus_bus_name_has_owner(headphoned
->session_bus
, PANUCCI_SRVC
, NULL
)) {
211 debug_msg("Sending pause signal to Panucci");
212 osso_rpc_run(headphoned
->osso
,
220 debug_msg("Panucci not running - not sending pause signal.");
224 if ((mplayer_fifo
= open(MPLAYER_FIFO
, O_WRONLY
| O_NONBLOCK
)) != -1) {
225 debug_msg("Sending pause signal to MPlayer");
226 write(mplayer_fifo
, "pause\n", 6);
229 debug_msg("MPlayer not running - not sending pause signal.");
233 if (dbus_bus_name_has_owner(headphoned
->session_bus
, FMRADIO_SRVC
, NULL
)) {
234 debug_msg("Sending pause signal to FM Radio");
235 osso_rpc_run(headphoned
->osso
,
245 if (dbus_bus_name_has_owner(headphoned
->session_bus
, MEDIABOX_SRVC
, NULL
)) {
246 debug_msg("Sending pause signal to MediaBox");
247 osso_rpc_run(headphoned
->osso
,
257 /* A convenient GSourceFunc wrapper for broadcast_pause_signal */
259 broadcast_pause_signal_later(gpointer data
)
261 Headphoned
*headphoned
= (Headphoned
*)data
;
262 broadcast_pause_signal(headphoned
);
266 /* Handler for messages from "wired" headphones (via sysfs) */
268 on_file_changed(GIOChannel
* source
, GIOCondition condition
, gpointer data
)
270 Headphoned
* headphoned
= (Headphoned
*)data
;
273 debug_msg("File %s has changed.", STATE_FILE
);
275 g_io_channel_seek_position(source
, 0, G_SEEK_SET
, NULL
);
276 g_io_channel_read_line(source
, &result
, NULL
, NULL
, NULL
);
279 if (headphoned
->initial
== TRUE
) {
280 debug_msg("Ignoring initial file change.");
281 headphoned
->initial
= FALSE
;
283 if (g_ascii_strcasecmp(result
, STATE_DISCONNECTED_STR
) == 0) {
284 debug_msg("Broadcasting pause signal (cause: state file)");
285 broadcast_pause_signal(headphoned
);
287 if (headphoned
->call_active
== TRUE
) {
289 * Remember this unplug event, so that we can
290 * pause players after the call is finished.
292 debug_msg("Setting flag: unplugged_on_call");
293 headphoned
->unplugged_on_call
= TRUE
;
302 /* Handler for messages from Bluetooth + "wired w/ mic" headphones */
303 static DBusHandlerResult
304 on_msg_recieved(DBusConnection
* connection G_GNUC_UNUSED
, DBusMessage
* message
, void* data
)
306 Headphoned
* headphoned
= (Headphoned
*)data
;
307 DBusMessageIter iter
;
308 const char* result
= NULL
;
310 dbus_message_iter_init(message
, &iter
);
311 dbus_message_iter_get_basic(&iter
, &result
);
313 if (g_str_equal(dbus_message_get_path(message
), HAL_MANAGER_PATH
)) {
314 if (g_str_equal(result
, HEADPHONE_UDI_NAME
)) {
315 debug_msg("Broadcasting pause signal (cause: Hal via D-Bus)");
316 broadcast_pause_signal(headphoned
);
317 return DBUS_HANDLER_RESULT_HANDLED
;
319 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
321 } else if (g_str_equal(dbus_message_get_path(message
), MCE_SIGNAL_PATH
)) {
322 if (g_str_equal(result
, MCE_CALL_ACTIVE
) ||
323 g_str_equal(result
, MCE_CALL_RINGING
)) {
324 debug_msg("Active call detected.");
325 headphoned
->call_active
= TRUE
;
326 return DBUS_HANDLER_RESULT_HANDLED
;
327 } else if (g_str_equal(result
, MCE_CALL_DISCONNECT
)) {
328 debug_msg("Call disconnect detected.");
329 headphoned
->call_active
= FALSE
;
330 if (headphoned
->unplugged_on_call
== TRUE
) {
331 headphoned
->unplugged_on_call
= FALSE
;
332 debug_msg("Broadcasting pause signal (cause: call disconnect)");
333 g_timeout_add(POST_CALL_PAUSE_DELAY_MS
,
334 broadcast_pause_signal_later
,
335 (gpointer
)headphoned
);
337 return DBUS_HANDLER_RESULT_HANDLED
;
339 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
343 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
347 main(int argc
, char* argv
[])
349 Headphoned
*headphoned
= NULL
;
350 GIOChannel
* state
= NULL
;
352 signal(SIGINT
, sig_handler
);
353 signal(SIGQUIT
, sig_handler
);
354 signal(SIGTERM
, sig_handler
);
356 loop
= g_main_loop_new(NULL
, FALSE
);
357 headphoned
= headphoned_new();
359 state
= g_io_channel_new_file(STATE_FILE
, "r", NULL
);
361 debug_msg("Adding I/O watch on %s", STATE_FILE
);
362 g_io_add_watch(state
, G_IO_PRI
, on_file_changed
, headphoned
);
364 warning_msg("Cannot open state file: %s", STATE_FILE
);
367 debug_msg("Registering D-Bus rule: %s", DBUS_RULE_HAL
);
368 dbus_bus_add_match(headphoned
->system_bus
, DBUS_RULE_HAL
, NULL
);
370 debug_msg("Registering D-Bus rule: %s", DBUS_RULE_CALL
);
371 dbus_bus_add_match(headphoned
->system_bus
, DBUS_RULE_CALL
, NULL
);
373 dbus_connection_add_filter(headphoned
->system_bus
, on_msg_recieved
, headphoned
, NULL
);
375 debug_msg("Entering GLib main loop...");
376 g_main_loop_run(loop
);
377 debug_msg("...main loop finished. Cleaning up.");
380 g_io_channel_unref(state
);
383 pb_playback_destroy(headphoned
->playback
);
384 osso_deinitialize(headphoned
->osso
);