Merge lp:~sqrammi/bluez/bluez-bdremote into lp:bluez

Proposed by Jeff Hansen
Status: Needs review
Proposed branch: lp:~sqrammi/bluez/bluez-bdremote
Merge into: lp:bluez
Diff against target: 242159 lines (has conflicts)
Conflict adding file AUTHORS.  Moved existing file to AUTHORS.moved.
Conflict adding file COPYING.LIB.  Moved existing file to COPYING.LIB.moved.
Conflict adding file COPYING.  Moved existing file to COPYING.moved.
Conflict adding file ChangeLog.  Moved existing file to ChangeLog.moved.
Conflict adding file INSTALL.  Moved existing file to INSTALL.moved.
Conflict adding file Makefile.am.  Moved existing file to Makefile.am.moved.
Conflict adding file Makefile.tools.  Moved existing file to Makefile.tools.moved.
Conflict adding file NEWS.  Moved existing file to NEWS.moved.
Conflict adding file README.  Moved existing file to README.moved.
Conflict adding file TODO.  Moved existing file to TODO.moved.
Conflict adding file acinclude.m4.  Moved existing file to acinclude.m4.moved.
Conflict adding file attrib.  Moved existing file to attrib.moved.
Conflict adding file btio.  Moved existing file to btio.moved.
Conflict adding file configure.ac.  Moved existing file to configure.ac.moved.
Conflict adding file doc.  Moved existing file to doc.moved.
Conflict adding file gdbus.  Moved existing file to gdbus.moved.
Conflict adding file lib.  Moved existing file to lib.moved.
Conflict adding file plugins.  Moved existing file to plugins.moved.
Conflict adding file src.  Moved existing file to src.moved.
Conflict adding file test.  Moved existing file to test.moved.
Conflict adding file tools.  Moved existing file to tools.moved.
Conflict adding file unit.  Moved existing file to unit.moved.
To merge this branch: bzr merge lp:~sqrammi/bluez/bluez-bdremote
Reviewer Review Type Date Requested Status
VCS imports Pending
Review via email: mp+159747@code.launchpad.net

Description of the change

I have been using these patches on my gentoo boxes for a long time, and they make a PS3 remote actually usable.

To post a comment you must log in.
Revision history for this message
Jeff Hansen (sqrammi) wrote :

These just need to be merged for 4.98 (precise). Both of them have already been mainlined into BlueZ.

Unmerged revisions

98. By Jeff Hansen

debian/patches/13-fakehid-reconnect.patch: Allow fakehid device to
reconnect to uinput if it has lost its connection. Without this, a
bdremote is unable to reconnect to bluez if the batteries die, or it goes
out of range, etc.

97. By Jeff Hansen

debian/patches/12-bdremote-timeout.patch: Make ps3remote use the system-wide
input IdleTimeout, which dramatically increases battery life on PS3 remote.

96. By Mathieu Trudel-Lapierre

* debian/patches/10-unregister_interface_on_exit.patch: unregister the SAP
  interface on exit. Thanks to Jesse Sung for the patch.
* debian/patches/11-explicitly_close.patch: make sure the io channel for
  HFP is properly closed on exit. Thanks to Jesse Sung for the patch.
  (LP: #907818)

95. By Martin Pitt

debian/control: Drop unused python-gobject dependency.

94. By Evan Broder

Fix the version number in debian/bluez.maintscript so it actually
cleans up the file. (LP: #953448)

93. By Evan Broder

* Respawn bluetooth daemon if crashed.
* Eliminate the /etc/default/bluetooth conffile as it's not the Upstart
  way. Transition the BLUETOOTH_ENABLED variable to an Upstart override
  variable if it's been changed.

92. By Mathieu Trudel-Lapierre

debian/patches/enable_audio_profiles.patch: properly enable the Source
profile, its state is checked twice, once in audio/a2dp.c again which was
not taken into account in the previous upload. (LP: #948613)

91. By Evan Broder

Refresh changelog timestamp

90. By Evan Broder

debian/patches/enable_audio_profiles.patch: enable the Gateway and Source
audio profiles by default. (LP: #948613)

89. By Evan Broder

Bump the version number

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory '.pc'
2=== added file '.pc/.version'
3--- .pc/.version 1970-01-01 00:00:00 +0000
4+++ .pc/.version 2013-04-19 02:35:36 +0000
5@@ -0,0 +1,1 @@
6+2
7
8=== added directory '.pc/01_lower_sink_ranking.patch'
9=== added directory '.pc/01_lower_sink_ranking.patch/audio'
10=== added file '.pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c'
11--- .pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c 1970-01-01 00:00:00 +0000
12+++ .pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c 2013-04-19 02:35:36 +0000
13@@ -0,0 +1,730 @@
14+/*
15+ *
16+ * BlueZ - Bluetooth protocol stack for Linux
17+ *
18+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
19+ *
20+ *
21+ * This library is free software; you can redistribute it and/or
22+ * modify it under the terms of the GNU Lesser General Public
23+ * License as published by the Free Software Foundation; either
24+ * version 2.1 of the License, or (at your option) any later version.
25+ *
26+ * This library is distributed in the hope that it will be useful,
27+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
28+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29+ * Lesser General Public License for more details.
30+ *
31+ * You should have received a copy of the GNU Lesser General Public
32+ * License along with this library; if not, write to the Free Software
33+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
34+ *
35+ */
36+
37+#ifdef HAVE_CONFIG_H
38+#include <config.h>
39+#endif
40+
41+#include <unistd.h>
42+#include <pthread.h>
43+
44+#include "gstpragma.h"
45+#include "gsta2dpsink.h"
46+
47+GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
48+#define GST_CAT_DEFAULT gst_a2dp_sink_debug
49+
50+#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
51+#define TEMPLATE_MAX_BITPOOL_STR "64"
52+
53+#define DEFAULT_AUTOCONNECT TRUE
54+
55+enum {
56+ PROP_0,
57+ PROP_DEVICE,
58+ PROP_AUTOCONNECT,
59+ PROP_TRANSPORT
60+};
61+
62+GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
63+
64+static const GstElementDetails gst_a2dp_sink_details =
65+ GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
66+ "Sink/Audio",
67+ "Plays audio to an A2DP device",
68+ "Marcel Holtmann <marcel@holtmann.org>");
69+
70+static GstStaticPadTemplate gst_a2dp_sink_factory =
71+ GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
72+ GST_STATIC_CAPS("audio/x-sbc, "
73+ "rate = (int) { 16000, 32000, 44100, 48000 }, "
74+ "channels = (int) [ 1, 2 ], "
75+ "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
76+ "blocks = (int) { 4, 8, 12, 16 }, "
77+ "subbands = (int) { 4, 8 }, "
78+ "allocation = (string) { \"snr\", \"loudness\" }, "
79+ "bitpool = (int) [ 2, "
80+ TEMPLATE_MAX_BITPOOL_STR " ]; "
81+ "audio/mpeg"
82+ ));
83+
84+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
85+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
86+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
87+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
88+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self);
89+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self);
90+
91+static void gst_a2dp_sink_finalize(GObject *obj)
92+{
93+ GstA2dpSink *self = GST_A2DP_SINK(obj);
94+
95+ g_mutex_free(self->cb_mutex);
96+
97+ G_OBJECT_CLASS(parent_class)->finalize(obj);
98+}
99+
100+static GstState gst_a2dp_sink_get_state(GstA2dpSink *self)
101+{
102+ GstState current, pending;
103+
104+ gst_element_get_state(GST_ELEMENT(self), &current, &pending, 0);
105+ if (pending == GST_STATE_VOID_PENDING)
106+ return current;
107+
108+ return pending;
109+}
110+
111+/*
112+ * Helper function to create elements, add to the bin and link it
113+ * to another element.
114+ */
115+static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self,
116+ const gchar *elementname, const gchar *name,
117+ GstElement *link_to)
118+{
119+ GstElement *element;
120+ GstState state;
121+
122+ GST_LOG_OBJECT(self, "Initializing %s", elementname);
123+
124+ element = gst_element_factory_make(elementname, name);
125+ if (element == NULL) {
126+ GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname);
127+ return NULL;
128+ }
129+
130+ if (!gst_bin_add(GST_BIN(self), element)) {
131+ GST_DEBUG_OBJECT(self, "failed to add %s to the bin",
132+ elementname);
133+ goto cleanup_and_fail;
134+ }
135+
136+ state = gst_a2dp_sink_get_state(self);
137+ if (gst_element_set_state(element, state) ==
138+ GST_STATE_CHANGE_FAILURE) {
139+ GST_DEBUG_OBJECT(self, "%s failed to go to playing",
140+ elementname);
141+ goto remove_element_and_fail;
142+ }
143+
144+ if (link_to != NULL)
145+ if (!gst_element_link(link_to, element)) {
146+ GST_DEBUG_OBJECT(self, "couldn't link %s",
147+ elementname);
148+ goto remove_element_and_fail;
149+ }
150+
151+ return element;
152+
153+remove_element_and_fail:
154+ gst_element_set_state(element, GST_STATE_NULL);
155+ gst_bin_remove(GST_BIN(self), element);
156+ return NULL;
157+
158+cleanup_and_fail:
159+ g_object_unref(G_OBJECT(element));
160+
161+ return NULL;
162+}
163+
164+static void gst_a2dp_sink_base_init(gpointer g_class)
165+{
166+ GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
167+
168+ gst_element_class_set_details(element_class,
169+ &gst_a2dp_sink_details);
170+ gst_element_class_add_pad_template(element_class,
171+ gst_static_pad_template_get(&gst_a2dp_sink_factory));
172+}
173+
174+static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
175+ const GValue *value, GParamSpec *pspec)
176+{
177+ GstA2dpSink *self = GST_A2DP_SINK(object);
178+
179+ switch (prop_id) {
180+ case PROP_DEVICE:
181+ if (self->sink != NULL)
182+ gst_avdtp_sink_set_device(self->sink,
183+ g_value_get_string(value));
184+
185+ if (self->device != NULL)
186+ g_free(self->device);
187+ self->device = g_value_dup_string(value);
188+ break;
189+
190+ case PROP_TRANSPORT:
191+ if (self->sink != NULL)
192+ gst_avdtp_sink_set_transport(self->sink,
193+ g_value_get_string(value));
194+
195+ if (self->transport != NULL)
196+ g_free(self->transport);
197+ self->transport = g_value_dup_string(value);
198+ break;
199+
200+ case PROP_AUTOCONNECT:
201+ self->autoconnect = g_value_get_boolean(value);
202+
203+ if (self->sink != NULL)
204+ g_object_set(G_OBJECT(self->sink), "auto-connect",
205+ self->autoconnect, NULL);
206+ break;
207+
208+ default:
209+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
210+ break;
211+ }
212+}
213+
214+static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
215+ GValue *value, GParamSpec *pspec)
216+{
217+ GstA2dpSink *self = GST_A2DP_SINK(object);
218+ gchar *device, *transport;
219+
220+ switch (prop_id) {
221+ case PROP_DEVICE:
222+ if (self->sink != NULL) {
223+ device = gst_avdtp_sink_get_device(self->sink);
224+ if (device != NULL)
225+ g_value_take_string(value, device);
226+ }
227+ break;
228+ case PROP_AUTOCONNECT:
229+ if (self->sink != NULL)
230+ g_object_get(G_OBJECT(self->sink), "auto-connect",
231+ &self->autoconnect, NULL);
232+
233+ g_value_set_boolean(value, self->autoconnect);
234+ break;
235+ case PROP_TRANSPORT:
236+ if (self->sink != NULL) {
237+ transport = gst_avdtp_sink_get_transport(self->sink);
238+ if (transport != NULL)
239+ g_value_take_string(value, transport);
240+ }
241+ break;
242+ default:
243+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
244+ break;
245+ }
246+}
247+
248+static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
249+{
250+ GstPad *capsfilter_pad;
251+
252+ /* we search for the capsfilter sinkpad */
253+ capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
254+
255+ /* now we add a ghostpad */
256+ self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
257+ capsfilter_pad));
258+ g_object_unref(capsfilter_pad);
259+
260+ /* the getcaps of our ghostpad must reflect the device caps */
261+ gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
262+ gst_a2dp_sink_get_caps);
263+ self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
264+ gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
265+ GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
266+
267+ /* we need to handle events on our own and we also need the eventfunc
268+ * of the ghostpad for forwarding calls */
269+ self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
270+ gst_pad_set_event_function(GST_PAD(self->ghostpad),
271+ gst_a2dp_sink_handle_event);
272+
273+ if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
274+ GST_ERROR_OBJECT(self, "failed to add ghostpad");
275+
276+ return TRUE;
277+}
278+
279+static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
280+{
281+ if (self->rtp) {
282+ GST_LOG_OBJECT(self, "removing rtp element from the bin");
283+ if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
284+ GST_WARNING_OBJECT(self, "failed to remove rtp "
285+ "element from bin");
286+ else
287+ self->rtp = NULL;
288+ }
289+}
290+
291+static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
292+ GstStateChange transition)
293+{
294+ GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
295+ GstA2dpSink *self = GST_A2DP_SINK(element);
296+
297+ switch (transition) {
298+ case GST_STATE_CHANGE_READY_TO_PAUSED:
299+ self->taglist = gst_tag_list_new();
300+
301+ gst_a2dp_sink_init_fakesink(self);
302+ break;
303+
304+ case GST_STATE_CHANGE_NULL_TO_READY:
305+ self->sink_is_in_bin = FALSE;
306+ self->sink = GST_AVDTP_SINK(gst_element_factory_make(
307+ "avdtpsink", "avdtpsink"));
308+ if (self->sink == NULL) {
309+ GST_WARNING_OBJECT(self, "failed to create avdtpsink");
310+ return GST_STATE_CHANGE_FAILURE;
311+ }
312+
313+ if (self->device != NULL)
314+ gst_avdtp_sink_set_device(self->sink,
315+ self->device);
316+
317+ if (self->transport != NULL)
318+ gst_avdtp_sink_set_transport(self->sink,
319+ self->transport);
320+
321+ g_object_set(G_OBJECT(self->sink), "auto-connect",
322+ self->autoconnect, NULL);
323+
324+ ret = gst_element_set_state(GST_ELEMENT(self->sink),
325+ GST_STATE_READY);
326+ break;
327+ default:
328+ break;
329+ }
330+
331+ if (ret == GST_STATE_CHANGE_FAILURE)
332+ return ret;
333+
334+ ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
335+ transition);
336+
337+ switch (transition) {
338+ case GST_STATE_CHANGE_PAUSED_TO_READY:
339+ if (self->taglist) {
340+ gst_tag_list_free(self->taglist);
341+ self->taglist = NULL;
342+ }
343+ if (self->newseg_event != NULL) {
344+ gst_event_unref(self->newseg_event);
345+ self->newseg_event = NULL;
346+ }
347+ gst_a2dp_sink_remove_fakesink(self);
348+ break;
349+
350+ case GST_STATE_CHANGE_READY_TO_NULL:
351+ if (self->sink_is_in_bin) {
352+ if (!gst_bin_remove(GST_BIN(self),
353+ GST_ELEMENT(self->sink)))
354+ GST_WARNING_OBJECT(self, "Failed to remove "
355+ "avdtpsink from bin");
356+ } else if (self->sink != NULL) {
357+ gst_element_set_state(GST_ELEMENT(self->sink),
358+ GST_STATE_NULL);
359+ g_object_unref(G_OBJECT(self->sink));
360+ }
361+
362+ self->sink = NULL;
363+
364+ gst_a2dp_sink_remove_dynamic_elements(self);
365+ break;
366+ default:
367+ break;
368+ }
369+
370+ return ret;
371+}
372+
373+static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
374+{
375+ GObjectClass *object_class = G_OBJECT_CLASS(klass);
376+ GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
377+
378+ parent_class = g_type_class_peek_parent(klass);
379+
380+ object_class->set_property = GST_DEBUG_FUNCPTR(
381+ gst_a2dp_sink_set_property);
382+ object_class->get_property = GST_DEBUG_FUNCPTR(
383+ gst_a2dp_sink_get_property);
384+
385+ object_class->finalize = GST_DEBUG_FUNCPTR(
386+ gst_a2dp_sink_finalize);
387+
388+ element_class->change_state = GST_DEBUG_FUNCPTR(
389+ gst_a2dp_sink_change_state);
390+
391+ g_object_class_install_property(object_class, PROP_DEVICE,
392+ g_param_spec_string("device", "Device",
393+ "Bluetooth remote device address",
394+ NULL, G_PARAM_READWRITE));
395+
396+ g_object_class_install_property(object_class, PROP_AUTOCONNECT,
397+ g_param_spec_boolean("auto-connect", "Auto-connect",
398+ "Automatically attempt to connect to device",
399+ DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
400+
401+ g_object_class_install_property(object_class, PROP_TRANSPORT,
402+ g_param_spec_string("transport", "Transport",
403+ "Use configured transport",
404+ NULL, G_PARAM_READWRITE));
405+
406+ GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
407+ "A2DP sink element");
408+}
409+
410+GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
411+{
412+ return gst_avdtp_sink_get_device_caps(self->sink);
413+}
414+
415+static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
416+{
417+ GstCaps *caps;
418+ GstCaps *caps_aux;
419+ GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
420+
421+ if (self->sink == NULL) {
422+ GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
423+ "returning template caps");
424+ caps = gst_static_pad_template_get_caps(
425+ &gst_a2dp_sink_factory);
426+ } else {
427+ GST_LOG_OBJECT(self, "Getting device caps");
428+ caps = gst_a2dp_sink_get_device_caps(self);
429+ if (caps == NULL)
430+ caps = gst_static_pad_template_get_caps(
431+ &gst_a2dp_sink_factory);
432+ }
433+ caps_aux = gst_caps_copy(caps);
434+ g_object_set(self->capsfilter, "caps", caps_aux, NULL);
435+ gst_caps_unref(caps_aux);
436+ return caps;
437+}
438+
439+static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
440+{
441+ GstElement *sink;
442+
443+ /* check if we don't need a new sink */
444+ if (self->sink_is_in_bin)
445+ return TRUE;
446+
447+ if (self->sink == NULL)
448+ sink = gst_element_factory_make("avdtpsink", "avdtpsink");
449+ else
450+ sink = GST_ELEMENT(self->sink);
451+
452+ if (sink == NULL) {
453+ GST_ERROR_OBJECT(self, "Couldn't create avdtpsink");
454+ return FALSE;
455+ }
456+
457+ if (!gst_bin_add(GST_BIN(self), sink)) {
458+ GST_ERROR_OBJECT(self, "failed to add avdtpsink "
459+ "to the bin");
460+ goto cleanup_and_fail;
461+ }
462+
463+ if (gst_element_set_state(sink, GST_STATE_READY) ==
464+ GST_STATE_CHANGE_FAILURE) {
465+ GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready");
466+ goto remove_element_and_fail;
467+ }
468+
469+ if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
470+ GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
471+ "to avdtpsink");
472+ goto remove_element_and_fail;
473+ }
474+
475+ self->sink = GST_AVDTP_SINK(sink);
476+ self->sink_is_in_bin = TRUE;
477+ g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
478+ g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
479+
480+ gst_element_set_state(sink, GST_STATE_PAUSED);
481+
482+ return TRUE;
483+
484+remove_element_and_fail:
485+ gst_element_set_state(sink, GST_STATE_NULL);
486+ gst_bin_remove(GST_BIN(self), sink);
487+ return FALSE;
488+
489+cleanup_and_fail:
490+ if (sink != NULL)
491+ g_object_unref(G_OBJECT(sink));
492+
493+ return FALSE;
494+}
495+
496+static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
497+{
498+ GstElement *rtppay;
499+
500+ /* if we already have a rtp, we don't need a new one */
501+ if (self->rtp != NULL)
502+ return TRUE;
503+
504+ rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
505+ self->capsfilter);
506+ if (rtppay == NULL)
507+ return FALSE;
508+
509+ self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
510+ g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
511+
512+ gst_element_set_state(rtppay, GST_STATE_PAUSED);
513+
514+ return TRUE;
515+}
516+
517+static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
518+{
519+ GstElement *rtppay;
520+
521+ /* check if we don't need a new rtp */
522+ if (self->rtp)
523+ return TRUE;
524+
525+ GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
526+ /* if capsfilter is not created then we can't have our rtp element */
527+ if (self->capsfilter == NULL)
528+ return FALSE;
529+
530+ rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp",
531+ self->capsfilter);
532+ if (rtppay == NULL)
533+ return FALSE;
534+
535+ self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
536+
537+ gst_element_set_state(rtppay, GST_STATE_PAUSED);
538+
539+ return TRUE;
540+}
541+
542+static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
543+ GstCaps *caps)
544+{
545+ GstStructure *structure;
546+ GstEvent *event;
547+ GstPad *capsfilterpad;
548+ gboolean crc;
549+ gchar *mode = NULL;
550+
551+ structure = gst_caps_get_structure(caps, 0);
552+
553+ /* before everything we need to remove fakesink */
554+ gst_a2dp_sink_remove_fakesink(self);
555+
556+ /* first, we need to create our rtp payloader */
557+ if (gst_structure_has_name(structure, "audio/x-sbc")) {
558+ GST_LOG_OBJECT(self, "sbc media received");
559+ if (!gst_a2dp_sink_init_rtp_sbc_element(self))
560+ return FALSE;
561+ } else if (gst_structure_has_name(structure, "audio/mpeg")) {
562+ GST_LOG_OBJECT(self, "mp3 media received");
563+ if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
564+ return FALSE;
565+ } else {
566+ GST_ERROR_OBJECT(self, "Unexpected media type");
567+ return FALSE;
568+ }
569+
570+ if (!gst_a2dp_sink_init_avdtp_sink(self))
571+ return FALSE;
572+
573+ /* check if we should push the taglist FIXME should we push this?
574+ * we can send the tags directly if needed */
575+ if (self->taglist != NULL &&
576+ gst_structure_has_name(structure, "audio/mpeg")) {
577+
578+ event = gst_event_new_tag(self->taglist);
579+
580+ /* send directly the crc */
581+ if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
582+ gst_avdtp_sink_set_crc(self->sink, crc);
583+
584+ if (gst_tag_list_get_string(self->taglist, "channel-mode",
585+ &mode))
586+ gst_avdtp_sink_set_channel_mode(self->sink, mode);
587+
588+ capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
589+ gst_pad_send_event(capsfilterpad, event);
590+ self->taglist = NULL;
591+ g_free(mode);
592+ }
593+
594+ if (!gst_avdtp_sink_set_device_caps(self->sink, caps))
595+ return FALSE;
596+
597+ g_object_set(G_OBJECT(self->rtp), "mtu",
598+ gst_avdtp_sink_get_link_mtu(self->sink), NULL);
599+
600+ /* we forward our new segment here if we have one */
601+ if (self->newseg_event) {
602+ gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
603+ self->newseg_event);
604+ self->newseg_event = NULL;
605+ }
606+
607+ return TRUE;
608+}
609+
610+static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
611+{
612+ GstA2dpSink *self;
613+
614+ self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
615+ GST_INFO_OBJECT(self, "setting caps");
616+
617+ /* now we know the caps */
618+ gst_a2dp_sink_init_dynamic_elements(self, caps);
619+
620+ return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
621+}
622+
623+/* used for catching newsegment events while we don't have a sink, for
624+ * later forwarding it to the sink */
625+static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
626+{
627+ GstA2dpSink *self;
628+ GstTagList *taglist = NULL;
629+ GstObject *parent;
630+
631+ self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
632+ parent = gst_element_get_parent(GST_ELEMENT(self->sink));
633+
634+ if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
635+ parent != GST_OBJECT_CAST(self)) {
636+ if (self->newseg_event != NULL)
637+ gst_event_unref(self->newseg_event);
638+ self->newseg_event = gst_event_ref(event);
639+
640+ } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
641+ parent != GST_OBJECT_CAST(self)) {
642+ if (self->taglist == NULL)
643+ gst_event_parse_tag(event, &self->taglist);
644+ else {
645+ gst_event_parse_tag(event, &taglist);
646+ gst_tag_list_insert(self->taglist, taglist,
647+ GST_TAG_MERGE_REPLACE);
648+ }
649+ }
650+
651+ if (parent != NULL)
652+ gst_object_unref(GST_OBJECT(parent));
653+
654+ return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
655+}
656+
657+static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
658+{
659+ GstElement *element;
660+
661+ element = gst_element_factory_make("capsfilter", "filter");
662+ if (element == NULL)
663+ goto failed;
664+
665+ if (!gst_bin_add(GST_BIN(self), element))
666+ goto failed;
667+
668+ self->capsfilter = element;
669+ return TRUE;
670+
671+failed:
672+ GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
673+ return FALSE;
674+}
675+
676+static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self)
677+{
678+ if (self->fakesink != NULL)
679+ return TRUE;
680+
681+ g_mutex_lock(self->cb_mutex);
682+ self->fakesink = gst_a2dp_sink_init_element(self, "fakesink",
683+ "fakesink", self->capsfilter);
684+ g_mutex_unlock(self->cb_mutex);
685+
686+ if (!self->fakesink)
687+ return FALSE;
688+
689+ return TRUE;
690+}
691+
692+static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self)
693+{
694+ g_mutex_lock(self->cb_mutex);
695+
696+ if (self->fakesink != NULL) {
697+ gst_element_set_locked_state(self->fakesink, TRUE);
698+ gst_element_set_state(self->fakesink, GST_STATE_NULL);
699+
700+ gst_bin_remove(GST_BIN(self), self->fakesink);
701+ self->fakesink = NULL;
702+ }
703+
704+ g_mutex_unlock(self->cb_mutex);
705+
706+ return TRUE;
707+}
708+
709+static void gst_a2dp_sink_init(GstA2dpSink *self,
710+ GstA2dpSinkClass *klass)
711+{
712+ self->sink = NULL;
713+ self->fakesink = NULL;
714+ self->rtp = NULL;
715+ self->device = NULL;
716+ self->transport = NULL;
717+ self->autoconnect = DEFAULT_AUTOCONNECT;
718+ self->capsfilter = NULL;
719+ self->newseg_event = NULL;
720+ self->taglist = NULL;
721+ self->ghostpad = NULL;
722+ self->sink_is_in_bin = FALSE;
723+
724+ self->cb_mutex = g_mutex_new();
725+
726+ /* we initialize our capsfilter */
727+ gst_a2dp_sink_init_caps_filter(self);
728+ g_object_set(self->capsfilter, "caps",
729+ gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
730+ NULL);
731+
732+ gst_a2dp_sink_init_fakesink(self);
733+
734+ gst_a2dp_sink_init_ghost_pad(self);
735+
736+}
737+
738+gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin)
739+{
740+ return gst_element_register(plugin, "a2dpsink",
741+ GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
742+}
743+
744
745=== added directory '.pc/02_disable_hal.patch'
746=== added directory '.pc/02_disable_hal.patch/audio'
747=== added file '.pc/02_disable_hal.patch/audio/telephony-maemo5.c'
748--- .pc/02_disable_hal.patch/audio/telephony-maemo5.c 1970-01-01 00:00:00 +0000
749+++ .pc/02_disable_hal.patch/audio/telephony-maemo5.c 2013-04-19 02:35:36 +0000
750@@ -0,0 +1,2104 @@
751+/*
752+ *
753+ * BlueZ - Bluetooth protocol stack for Linux
754+ *
755+ * Copyright (C) 2008-2010 Nokia Corporation
756+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
757+ *
758+ *
759+ * This program is free software; you can redistribute it and/or modify
760+ * it under the terms of the GNU General Public License as published by
761+ * the Free Software Foundation; either version 2 of the License, or
762+ * (at your option) any later version.
763+ *
764+ * This program is distributed in the hope that it will be useful,
765+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
766+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
767+ * GNU General Public License for more details.
768+ *
769+ * You should have received a copy of the GNU General Public License
770+ * along with this program; if not, write to the Free Software
771+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
772+ *
773+ */
774+
775+#ifdef HAVE_CONFIG_H
776+#include <config.h>
777+#endif
778+
779+#include <stdlib.h>
780+#include <stdio.h>
781+#include <unistd.h>
782+#include <fcntl.h>
783+#include <stdint.h>
784+#include <string.h>
785+#include <glib.h>
786+#include <dbus/dbus.h>
787+#include <gdbus.h>
788+
789+#include "log.h"
790+#include "telephony.h"
791+#include "error.h"
792+
793+/* SSC D-Bus definitions */
794+#define SSC_DBUS_NAME "com.nokia.phone.SSC"
795+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
796+#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
797+
798+/* libcsnet D-Bus definitions */
799+#define NETWORK_BUS_NAME "com.nokia.phone.net"
800+#define NETWORK_INTERFACE "Phone.Net"
801+#define NETWORK_PATH "/com/nokia/phone/net"
802+
803+/* Mask bits for supported services */
804+#define NETWORK_MASK_GPRS_SUPPORT 0x01
805+#define NETWORK_MASK_CS_SERVICES 0x02
806+#define NETWORK_MASK_EGPRS_SUPPORT 0x04
807+#define NETWORK_MASK_HSDPA_AVAIL 0x08
808+#define NETWORK_MASK_HSUPA_AVAIL 0x10
809+
810+/* network get cell info: cell type */
811+#define NETWORK_UNKNOWN_CELL 0
812+#define NETWORK_GSM_CELL 1
813+#define NETWORK_WCDMA_CELL 2
814+
815+enum net_registration_status {
816+ NETWORK_REG_STATUS_HOME = 0x00,
817+ NETWORK_REG_STATUS_ROAM,
818+ NETWORK_REG_STATUS_ROAM_BLINK,
819+ NETWORK_REG_STATUS_NOSERV,
820+ NETWORK_REG_STATUS_NOSERV_SEARCHING,
821+ NETWORK_REG_STATUS_NOSERV_NOTSEARCHING,
822+ NETWORK_REG_STATUS_NOSERV_NOSIM,
823+ NETWORK_REG_STATUS_POWER_OFF = 0x08,
824+ NETWORK_REG_STATUS_NSPS,
825+ NETWORK_REG_STATUS_NSPS_NO_COVERAGE,
826+ NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW
827+};
828+
829+enum network_types {
830+ NETWORK_GSM_HOME_PLMN = 0,
831+ NETWORK_GSM_PREFERRED_PLMN,
832+ NETWORK_GSM_FORBIDDEN_PLMN,
833+ NETWORK_GSM_OTHER_PLMN,
834+ NETWORK_GSM_NO_PLMN_AVAIL
835+};
836+
837+enum network_alpha_tag_name_type {
838+ NETWORK_HARDCODED_LATIN_OPER_NAME = 0,
839+ NETWORK_HARDCODED_USC2_OPER_NAME,
840+ NETWORK_NITZ_SHORT_OPER_NAME,
841+ NETWORK_NITZ_FULL_OPER_NAME,
842+};
843+
844+#define TELEPHONY_MAEMO_PATH "/com/nokia/MaemoTelephony"
845+#define TELEPHONY_MAEMO_INTERFACE "com.nokia.MaemoTelephony"
846+
847+#define CALLERID_BASE "/var/lib/bluetooth/maemo-callerid-"
848+#define ALLOWED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-allowed"
849+#define RESTRICTED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-restricted"
850+#define NONE_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-none"
851+
852+static uint32_t callerid = 0;
853+
854+/* CSD CALL plugin D-Bus definitions */
855+#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
856+#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
857+#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
858+#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
859+#define CSD_CALL_PATH "/com/nokia/csd/call"
860+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
861+
862+/* Call status values as exported by the CSD CALL plugin */
863+#define CSD_CALL_STATUS_IDLE 0
864+#define CSD_CALL_STATUS_CREATE 1
865+#define CSD_CALL_STATUS_COMING 2
866+#define CSD_CALL_STATUS_PROCEEDING 3
867+#define CSD_CALL_STATUS_MO_ALERTING 4
868+#define CSD_CALL_STATUS_MT_ALERTING 5
869+#define CSD_CALL_STATUS_WAITING 6
870+#define CSD_CALL_STATUS_ANSWERED 7
871+#define CSD_CALL_STATUS_ACTIVE 8
872+#define CSD_CALL_STATUS_MO_RELEASE 9
873+#define CSD_CALL_STATUS_MT_RELEASE 10
874+#define CSD_CALL_STATUS_HOLD_INITIATED 11
875+#define CSD_CALL_STATUS_HOLD 12
876+#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
877+#define CSD_CALL_STATUS_RECONNECT_PENDING 14
878+#define CSD_CALL_STATUS_TERMINATED 15
879+#define CSD_CALL_STATUS_SWAP_INITIATED 16
880+
881+#define CALL_FLAG_NONE 0
882+#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
883+#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
884+
885+/* SIM Phonebook D-Bus definitions */
886+#define SIM_PHONEBOOK_BUS_NAME "com.nokia.phone.SIM"
887+#define SIM_PHONEBOOK_INTERFACE "Phone.Sim.Phonebook"
888+#define SIM_PHONEBOOK_PATH "/com/nokia/phone/SIM/phonebook"
889+
890+#define PHONEBOOK_INDEX_FIRST_ENTRY 0xFFFF
891+#define PHONEBOOK_INDEX_NEXT_FREE_LOCATION 0xFFFE
892+
893+enum sim_phonebook_type {
894+ SIM_PHONEBOOK_TYPE_ADN = 0x0,
895+ SIM_PHONEBOOK_TYPE_SDN,
896+ SIM_PHONEBOOK_TYPE_FDN,
897+ SIM_PHONEBOOK_TYPE_VMBX,
898+ SIM_PHONEBOOK_TYPE_MBDN,
899+ SIM_PHONEBOOK_TYPE_EN,
900+ SIM_PHONEBOOK_TYPE_MSISDN
901+};
902+
903+enum sim_phonebook_location_type {
904+ SIM_PHONEBOOK_LOCATION_EXACT = 0x0,
905+ SIM_PHONEBOOK_LOCATION_NEXT
906+};
907+
908+struct csd_call {
909+ char *object_path;
910+ int status;
911+ gboolean originating;
912+ gboolean emergency;
913+ gboolean on_hold;
914+ gboolean conference;
915+ char *number;
916+ gboolean setup;
917+};
918+
919+static struct {
920+ uint8_t status;
921+ uint16_t lac;
922+ uint32_t cell_id;
923+ uint32_t operator_code;
924+ uint32_t country_code;
925+ uint8_t network_type;
926+ uint8_t supported_services;
927+ uint16_t signals_bar;
928+ char *operator_name;
929+} net = {
930+ .status = NETWORK_REG_STATUS_NOSERV,
931+ .lac = 0,
932+ .cell_id = 0,
933+ .operator_code = 0,
934+ .country_code = 0,
935+ .network_type = NETWORK_GSM_NO_PLMN_AVAIL,
936+ .supported_services = 0,
937+ .signals_bar = 0,
938+ .operator_name = NULL,
939+};
940+
941+static DBusConnection *connection = NULL;
942+
943+static GSList *calls = NULL;
944+
945+/* Reference count for determining the call indicator status */
946+static GSList *active_calls = NULL;
947+
948+static char *msisdn = NULL; /* Subscriber number */
949+static char *vmbx = NULL; /* Voice mailbox number */
950+
951+/* HAL battery namespace key values */
952+static int battchg_cur = -1; /* "battery.charge_level.current" */
953+static int battchg_last = -1; /* "battery.charge_level.last_full" */
954+static int battchg_design = -1; /* "battery.charge_level.design" */
955+
956+static gboolean get_calls_active = FALSE;
957+
958+static gboolean events_enabled = FALSE;
959+
960+/* Supported set of call hold operations */
961+static const char *chld_str = "0,1,1x,2,2x,3,4";
962+
963+static char *last_dialed_number = NULL;
964+
965+/* Timer for tracking call creation requests */
966+static guint create_request_timer = 0;
967+
968+static struct indicator maemo_indicators[] =
969+{
970+ { "battchg", "0-5", 5, TRUE },
971+ { "signal", "0-5", 0, TRUE },
972+ { "service", "0,1", 0, TRUE },
973+ { "call", "0,1", 0, TRUE },
974+ { "callsetup", "0-3", 0, TRUE },
975+ { "callheld", "0-2", 0, FALSE },
976+ { "roam", "0,1", 0, TRUE },
977+ { NULL }
978+};
979+
980+static char *call_status_str[] = {
981+ "IDLE",
982+ "CREATE",
983+ "COMING",
984+ "PROCEEDING",
985+ "MO_ALERTING",
986+ "MT_ALERTING",
987+ "WAITING",
988+ "ANSWERED",
989+ "ACTIVE",
990+ "MO_RELEASE",
991+ "MT_RELEASE",
992+ "HOLD_INITIATED",
993+ "HOLD",
994+ "RETRIEVE_INITIATED",
995+ "RECONNECT_PENDING",
996+ "TERMINATED",
997+ "SWAP_INITIATED",
998+ "???"
999+};
1000+
1001+static struct csd_call *find_call(const char *path)
1002+{
1003+ GSList *l;
1004+
1005+ for (l = calls; l != NULL; l = l->next) {
1006+ struct csd_call *call = l->data;
1007+
1008+ if (g_str_equal(call->object_path, path))
1009+ return call;
1010+ }
1011+
1012+ return NULL;
1013+}
1014+
1015+static struct csd_call *find_non_held_call(void)
1016+{
1017+ GSList *l;
1018+
1019+ for (l = calls; l != NULL; l = l->next) {
1020+ struct csd_call *call = l->data;
1021+
1022+ if (call->status == CSD_CALL_STATUS_IDLE)
1023+ continue;
1024+
1025+ if (call->status != CSD_CALL_STATUS_HOLD)
1026+ return call;
1027+ }
1028+
1029+ return NULL;
1030+}
1031+
1032+static struct csd_call *find_non_idle_call(void)
1033+{
1034+ GSList *l;
1035+
1036+ for (l = calls; l != NULL; l = l->next) {
1037+ struct csd_call *call = l->data;
1038+
1039+ if (call->status != CSD_CALL_STATUS_IDLE)
1040+ return call;
1041+ }
1042+
1043+ return NULL;
1044+}
1045+
1046+static struct csd_call *find_call_with_status(int status)
1047+{
1048+ GSList *l;
1049+
1050+ for (l = calls; l != NULL; l = l->next) {
1051+ struct csd_call *call = l->data;
1052+
1053+ if (call->status == status)
1054+ return call;
1055+ }
1056+
1057+ return NULL;
1058+}
1059+
1060+static int release_conference(void)
1061+{
1062+ DBusMessage *msg;
1063+
1064+ DBG("telephony-maemo: releasing conference call");
1065+
1066+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
1067+ CSD_CALL_CONFERENCE_PATH,
1068+ CSD_CALL_INSTANCE,
1069+ "Release");
1070+ if (!msg) {
1071+ error("Unable to allocate new D-Bus message");
1072+ return -ENOMEM;
1073+ }
1074+
1075+ g_dbus_send_message(connection, msg);
1076+
1077+ return 0;
1078+}
1079+
1080+static int release_call(struct csd_call *call)
1081+{
1082+ DBusMessage *msg;
1083+
1084+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
1085+ call->object_path,
1086+ CSD_CALL_INSTANCE,
1087+ "Release");
1088+ if (!msg) {
1089+ error("Unable to allocate new D-Bus message");
1090+ return -ENOMEM;
1091+ }
1092+
1093+ g_dbus_send_message(connection, msg);
1094+
1095+ return 0;
1096+}
1097+
1098+static int answer_call(struct csd_call *call)
1099+{
1100+ DBusMessage *msg;
1101+
1102+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
1103+ call->object_path,
1104+ CSD_CALL_INSTANCE,
1105+ "Answer");
1106+ if (!msg) {
1107+ error("Unable to allocate new D-Bus message");
1108+ return -ENOMEM;
1109+ }
1110+
1111+ g_dbus_send_message(connection, msg);
1112+
1113+ return 0;
1114+}
1115+
1116+static int split_call(struct csd_call *call)
1117+{
1118+ DBusMessage *msg;
1119+
1120+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
1121+ call->object_path,
1122+ CSD_CALL_INSTANCE,
1123+ "Split");
1124+ if (!msg) {
1125+ error("Unable to allocate new D-Bus message");
1126+ return -ENOMEM;
1127+ }
1128+
1129+ g_dbus_send_message(connection, msg);
1130+
1131+ return 0;
1132+}
1133+
1134+static int unhold_call(struct csd_call *call)
1135+{
1136+ DBusMessage *msg;
1137+
1138+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1139+ CSD_CALL_INTERFACE,
1140+ "Unhold");
1141+ if (!msg) {
1142+ error("Unable to allocate new D-Bus message");
1143+ return -ENOMEM;
1144+ }
1145+
1146+ g_dbus_send_message(connection, msg);
1147+
1148+ return 0;
1149+}
1150+
1151+static int hold_call(struct csd_call *call)
1152+{
1153+ DBusMessage *msg;
1154+
1155+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1156+ CSD_CALL_INTERFACE,
1157+ "Hold");
1158+ if (!msg) {
1159+ error("Unable to allocate new D-Bus message");
1160+ return -ENOMEM;
1161+ }
1162+
1163+ g_dbus_send_message(connection, msg);
1164+
1165+ return 0;
1166+}
1167+
1168+static int swap_calls(void)
1169+{
1170+ DBusMessage *msg;
1171+
1172+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1173+ CSD_CALL_INTERFACE,
1174+ "Swap");
1175+ if (!msg) {
1176+ error("Unable to allocate new D-Bus message");
1177+ return -ENOMEM;
1178+ }
1179+
1180+ g_dbus_send_message(connection, msg);
1181+
1182+ return 0;
1183+}
1184+
1185+static int create_conference(void)
1186+{
1187+ DBusMessage *msg;
1188+
1189+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1190+ CSD_CALL_INTERFACE,
1191+ "Conference");
1192+ if (!msg) {
1193+ error("Unable to allocate new D-Bus message");
1194+ return -ENOMEM;
1195+ }
1196+
1197+ g_dbus_send_message(connection, msg);
1198+
1199+ return 0;
1200+}
1201+
1202+static int call_transfer(void)
1203+{
1204+ DBusMessage *msg;
1205+
1206+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1207+ CSD_CALL_INTERFACE,
1208+ "Transfer");
1209+ if (!msg) {
1210+ error("Unable to allocate new D-Bus message");
1211+ return -ENOMEM;
1212+ }
1213+
1214+ g_dbus_send_message(connection, msg);
1215+
1216+ return 0;
1217+}
1218+
1219+static int number_type(const char *number)
1220+{
1221+ if (number == NULL)
1222+ return NUMBER_TYPE_TELEPHONY;
1223+
1224+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
1225+ return NUMBER_TYPE_INTERNATIONAL;
1226+
1227+ return NUMBER_TYPE_TELEPHONY;
1228+}
1229+
1230+void telephony_device_connected(void *telephony_device)
1231+{
1232+ struct csd_call *coming;
1233+
1234+ DBG("telephony-maemo: device %p connected", telephony_device);
1235+
1236+ coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
1237+ if (coming) {
1238+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
1239+ telephony_call_waiting_ind(coming->number,
1240+ number_type(coming->number));
1241+ else
1242+ telephony_incoming_call_ind(coming->number,
1243+ number_type(coming->number));
1244+ }
1245+}
1246+
1247+void telephony_device_disconnected(void *telephony_device)
1248+{
1249+ DBG("telephony-maemo: device %p disconnected", telephony_device);
1250+ events_enabled = FALSE;
1251+}
1252+
1253+void telephony_event_reporting_req(void *telephony_device, int ind)
1254+{
1255+ events_enabled = ind == 1 ? TRUE : FALSE;
1256+
1257+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
1258+}
1259+
1260+void telephony_response_and_hold_req(void *telephony_device, int rh)
1261+{
1262+ telephony_response_and_hold_rsp(telephony_device,
1263+ CME_ERROR_NOT_SUPPORTED);
1264+}
1265+
1266+void telephony_last_dialed_number_req(void *telephony_device)
1267+{
1268+ DBG("telephony-maemo: last dialed number request");
1269+
1270+ if (last_dialed_number)
1271+ telephony_dial_number_req(telephony_device,
1272+ last_dialed_number);
1273+ else
1274+ telephony_last_dialed_number_rsp(telephony_device,
1275+ CME_ERROR_NOT_ALLOWED);
1276+}
1277+
1278+void telephony_terminate_call_req(void *telephony_device)
1279+{
1280+ struct csd_call *call;
1281+ int err;
1282+
1283+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1284+ if (!call)
1285+ call = find_non_idle_call();
1286+
1287+ if (!call) {
1288+ error("No active call");
1289+ telephony_terminate_call_rsp(telephony_device,
1290+ CME_ERROR_NOT_ALLOWED);
1291+ return;
1292+ }
1293+
1294+ if (call->conference)
1295+ err = release_conference();
1296+ else
1297+ err = release_call(call);
1298+
1299+ if (err < 0)
1300+ telephony_terminate_call_rsp(telephony_device,
1301+ CME_ERROR_AG_FAILURE);
1302+ else
1303+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
1304+}
1305+
1306+void telephony_answer_call_req(void *telephony_device)
1307+{
1308+ struct csd_call *call;
1309+
1310+ call = find_call_with_status(CSD_CALL_STATUS_COMING);
1311+ if (!call)
1312+ call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
1313+
1314+ if (!call)
1315+ call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
1316+
1317+ if (!call)
1318+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
1319+
1320+ if (!call) {
1321+ telephony_answer_call_rsp(telephony_device,
1322+ CME_ERROR_NOT_ALLOWED);
1323+ return;
1324+ }
1325+
1326+ if (answer_call(call) < 0)
1327+ telephony_answer_call_rsp(telephony_device,
1328+ CME_ERROR_AG_FAILURE);
1329+ else
1330+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
1331+}
1332+
1333+static int send_method_call(const char *dest, const char *path,
1334+ const char *interface, const char *method,
1335+ DBusPendingCallNotifyFunction cb,
1336+ void *user_data, int type, ...)
1337+{
1338+ DBusMessage *msg;
1339+ DBusPendingCall *call;
1340+ va_list args;
1341+
1342+ msg = dbus_message_new_method_call(dest, path, interface, method);
1343+ if (!msg) {
1344+ error("Unable to allocate new D-Bus %s message", method);
1345+ return -ENOMEM;
1346+ }
1347+
1348+ va_start(args, type);
1349+
1350+ if (!dbus_message_append_args_valist(msg, type, args)) {
1351+ dbus_message_unref(msg);
1352+ va_end(args);
1353+ return -EIO;
1354+ }
1355+
1356+ va_end(args);
1357+
1358+ if (!cb) {
1359+ g_dbus_send_message(connection, msg);
1360+ return 0;
1361+ }
1362+
1363+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
1364+ error("Sending %s failed", method);
1365+ dbus_message_unref(msg);
1366+ return -EIO;
1367+ }
1368+
1369+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
1370+ dbus_pending_call_unref(call);
1371+ dbus_message_unref(msg);
1372+
1373+ return 0;
1374+}
1375+
1376+static const char *memory_dial_lookup(int location)
1377+{
1378+ if (location == 1)
1379+ return vmbx;
1380+ else
1381+ return NULL;
1382+}
1383+
1384+void telephony_dial_number_req(void *telephony_device, const char *number)
1385+{
1386+ uint32_t flags = callerid;
1387+ int ret;
1388+
1389+ DBG("telephony-maemo: dial request to %s", number);
1390+
1391+ if (strncmp(number, "*31#", 4) == 0) {
1392+ number += 4;
1393+ flags = CALL_FLAG_PRESENTATION_ALLOWED;
1394+ } else if (strncmp(number, "#31#", 4) == 0) {
1395+ number += 4;
1396+ flags = CALL_FLAG_PRESENTATION_RESTRICTED;
1397+ } else if (number[0] == '>') {
1398+ const char *location = &number[1];
1399+
1400+ number = memory_dial_lookup(strtol(&number[1], NULL, 0));
1401+ if (!number) {
1402+ error("No number at memory location %s", location);
1403+ telephony_dial_number_rsp(telephony_device,
1404+ CME_ERROR_INVALID_INDEX);
1405+ return;
1406+ }
1407+ }
1408+
1409+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1410+ CSD_CALL_INTERFACE, "CreateWith",
1411+ NULL, NULL,
1412+ DBUS_TYPE_STRING, &number,
1413+ DBUS_TYPE_UINT32, &flags,
1414+ DBUS_TYPE_INVALID);
1415+ if (ret < 0) {
1416+ telephony_dial_number_rsp(telephony_device,
1417+ CME_ERROR_AG_FAILURE);
1418+ return;
1419+ }
1420+
1421+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
1422+}
1423+
1424+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
1425+{
1426+ int ret;
1427+ char buf[2] = { tone, '\0' }, *buf_ptr = buf;
1428+
1429+ DBG("telephony-maemo: transmit dtmf: %s", buf);
1430+
1431+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1432+ CSD_CALL_INTERFACE, "SendDTMF",
1433+ NULL, NULL,
1434+ DBUS_TYPE_STRING, &buf_ptr,
1435+ DBUS_TYPE_INVALID);
1436+ if (ret < 0) {
1437+ telephony_transmit_dtmf_rsp(telephony_device,
1438+ CME_ERROR_AG_FAILURE);
1439+ return;
1440+ }
1441+
1442+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
1443+}
1444+
1445+void telephony_subscriber_number_req(void *telephony_device)
1446+{
1447+ DBG("telephony-maemo: subscriber number request");
1448+ if (msisdn)
1449+ telephony_subscriber_number_ind(msisdn,
1450+ number_type(msisdn),
1451+ SUBSCRIBER_SERVICE_VOICE);
1452+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
1453+}
1454+
1455+static int csd_status_to_hfp(struct csd_call *call)
1456+{
1457+ switch (call->status) {
1458+ case CSD_CALL_STATUS_IDLE:
1459+ case CSD_CALL_STATUS_MO_RELEASE:
1460+ case CSD_CALL_STATUS_MT_RELEASE:
1461+ case CSD_CALL_STATUS_TERMINATED:
1462+ return -1;
1463+ case CSD_CALL_STATUS_CREATE:
1464+ return CALL_STATUS_DIALING;
1465+ case CSD_CALL_STATUS_WAITING:
1466+ return CALL_STATUS_WAITING;
1467+ case CSD_CALL_STATUS_PROCEEDING:
1468+ /* PROCEEDING can happen in outgoing/incoming */
1469+ if (call->originating)
1470+ return CALL_STATUS_DIALING;
1471+ else
1472+ return CALL_STATUS_INCOMING;
1473+ case CSD_CALL_STATUS_COMING:
1474+ return CALL_STATUS_INCOMING;
1475+ case CSD_CALL_STATUS_MO_ALERTING:
1476+ return CALL_STATUS_ALERTING;
1477+ case CSD_CALL_STATUS_MT_ALERTING:
1478+ return CALL_STATUS_INCOMING;
1479+ case CSD_CALL_STATUS_ANSWERED:
1480+ case CSD_CALL_STATUS_ACTIVE:
1481+ case CSD_CALL_STATUS_RECONNECT_PENDING:
1482+ case CSD_CALL_STATUS_SWAP_INITIATED:
1483+ case CSD_CALL_STATUS_HOLD_INITIATED:
1484+ return CALL_STATUS_ACTIVE;
1485+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1486+ case CSD_CALL_STATUS_HOLD:
1487+ return CALL_STATUS_HELD;
1488+ default:
1489+ return -1;
1490+ }
1491+}
1492+
1493+void telephony_list_current_calls_req(void *telephony_device)
1494+{
1495+ GSList *l;
1496+ int i;
1497+
1498+ DBG("telephony-maemo: list current calls request");
1499+
1500+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
1501+ struct csd_call *call = l->data;
1502+ int status, direction, multiparty;
1503+
1504+ status = csd_status_to_hfp(call);
1505+ if (status < 0)
1506+ continue;
1507+
1508+ direction = call->originating ?
1509+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
1510+
1511+ multiparty = call->conference ?
1512+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
1513+
1514+ telephony_list_current_call_ind(i, direction, status,
1515+ CALL_MODE_VOICE, multiparty,
1516+ call->number,
1517+ number_type(call->number));
1518+ }
1519+
1520+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
1521+}
1522+
1523+void telephony_operator_selection_req(void *telephony_device)
1524+{
1525+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
1526+ net.operator_name ? net.operator_name : "");
1527+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
1528+}
1529+
1530+static void foreach_call_with_status(int status,
1531+ int (*func)(struct csd_call *call))
1532+{
1533+ GSList *l;
1534+
1535+ for (l = calls; l != NULL; l = l->next) {
1536+ struct csd_call *call = l->data;
1537+
1538+ if (call->status == status)
1539+ func(call);
1540+ }
1541+}
1542+
1543+void telephony_call_hold_req(void *telephony_device, const char *cmd)
1544+{
1545+ const char *idx;
1546+ struct csd_call *call;
1547+ int err = 0;
1548+
1549+ DBG("telephony-maemo: got call hold request %s", cmd);
1550+
1551+ if (strlen(cmd) > 1)
1552+ idx = &cmd[1];
1553+ else
1554+ idx = NULL;
1555+
1556+ if (idx)
1557+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
1558+ else
1559+ call = NULL;
1560+
1561+ switch (cmd[0]) {
1562+ case '0':
1563+ foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call);
1564+ foreach_call_with_status(CSD_CALL_STATUS_WAITING,
1565+ release_call);
1566+ break;
1567+ case '1':
1568+ if (idx) {
1569+ if (call)
1570+ err = release_call(call);
1571+ break;
1572+ }
1573+ foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
1574+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
1575+ if (call)
1576+ err = answer_call(call);
1577+ break;
1578+ case '2':
1579+ if (idx) {
1580+ if (call)
1581+ err = split_call(call);
1582+ } else {
1583+ struct csd_call *held, *wait;
1584+
1585+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1586+ held = find_call_with_status(CSD_CALL_STATUS_HOLD);
1587+ wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
1588+
1589+ if (wait)
1590+ err = answer_call(wait);
1591+ else if (call && held)
1592+ err = swap_calls();
1593+ else {
1594+ if (call)
1595+ err = hold_call(call);
1596+ if (held)
1597+ err = unhold_call(held);
1598+ }
1599+ }
1600+ break;
1601+ case '3':
1602+ if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
1603+ find_call_with_status(CSD_CALL_STATUS_WAITING))
1604+ err = create_conference();
1605+ break;
1606+ case '4':
1607+ err = call_transfer();
1608+ break;
1609+ default:
1610+ DBG("Unknown call hold request");
1611+ break;
1612+ }
1613+
1614+ if (err)
1615+ telephony_call_hold_rsp(telephony_device,
1616+ CME_ERROR_AG_FAILURE);
1617+ else
1618+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
1619+}
1620+
1621+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
1622+{
1623+ DBG("telephony-maemo: got %s NR and EC request",
1624+ enable ? "enable" : "disable");
1625+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
1626+}
1627+
1628+void telephony_key_press_req(void *telephony_device, const char *keys)
1629+{
1630+ struct csd_call *active, *waiting;
1631+ int err;
1632+
1633+ DBG("telephony-maemo: got key press request for %s", keys);
1634+
1635+ waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
1636+ if (!waiting)
1637+ waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
1638+ if (!waiting)
1639+ waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
1640+
1641+ active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1642+
1643+ if (waiting)
1644+ err = answer_call(waiting);
1645+ else if (active)
1646+ err = release_call(active);
1647+ else
1648+ err = 0;
1649+
1650+ if (err < 0)
1651+ telephony_key_press_rsp(telephony_device,
1652+ CME_ERROR_AG_FAILURE);
1653+ else
1654+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
1655+}
1656+
1657+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
1658+{
1659+ DBG("telephony-maemo: got %s voice dial request",
1660+ enable ? "enable" : "disable");
1661+
1662+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
1663+}
1664+
1665+static void handle_incoming_call(DBusMessage *msg)
1666+{
1667+ const char *number, *call_path;
1668+ struct csd_call *call;
1669+
1670+ if (!dbus_message_get_args(msg, NULL,
1671+ DBUS_TYPE_OBJECT_PATH, &call_path,
1672+ DBUS_TYPE_STRING, &number,
1673+ DBUS_TYPE_INVALID)) {
1674+ error("Unexpected parameters in Call.Coming() signal");
1675+ return;
1676+ }
1677+
1678+ call = find_call(call_path);
1679+ if (!call) {
1680+ error("Didn't find any matching call object for %s",
1681+ call_path);
1682+ return;
1683+ }
1684+
1685+ DBG("Incoming call to %s from number %s", call_path, number);
1686+
1687+ g_free(call->number);
1688+ call->number = g_strdup(number);
1689+
1690+ telephony_update_indicator(maemo_indicators, "callsetup",
1691+ EV_CALLSETUP_INCOMING);
1692+
1693+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
1694+ telephony_call_waiting_ind(call->number,
1695+ number_type(call->number));
1696+ else
1697+ telephony_incoming_call_ind(call->number,
1698+ number_type(call->number));
1699+}
1700+
1701+static void handle_outgoing_call(DBusMessage *msg)
1702+{
1703+ const char *number, *call_path;
1704+ struct csd_call *call;
1705+
1706+ if (!dbus_message_get_args(msg, NULL,
1707+ DBUS_TYPE_OBJECT_PATH, &call_path,
1708+ DBUS_TYPE_STRING, &number,
1709+ DBUS_TYPE_INVALID)) {
1710+ error("Unexpected parameters in Call.Created() signal");
1711+ return;
1712+ }
1713+
1714+ call = find_call(call_path);
1715+ if (!call) {
1716+ error("Didn't find any matching call object for %s",
1717+ call_path);
1718+ return;
1719+ }
1720+
1721+ DBG("Outgoing call from %s to number %s", call_path, number);
1722+
1723+ g_free(call->number);
1724+ call->number = g_strdup(number);
1725+
1726+ g_free(last_dialed_number);
1727+ last_dialed_number = g_strdup(number);
1728+
1729+ if (create_request_timer) {
1730+ g_source_remove(create_request_timer);
1731+ create_request_timer = 0;
1732+ }
1733+}
1734+
1735+static gboolean create_timeout(gpointer user_data)
1736+{
1737+ telephony_update_indicator(maemo_indicators, "callsetup",
1738+ EV_CALLSETUP_INACTIVE);
1739+ create_request_timer = 0;
1740+ return FALSE;
1741+}
1742+
1743+static void handle_create_requested(DBusMessage *msg)
1744+{
1745+ DBG("Call.CreateRequested()");
1746+
1747+ if (create_request_timer)
1748+ g_source_remove(create_request_timer);
1749+
1750+ create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
1751+
1752+ telephony_update_indicator(maemo_indicators, "callsetup",
1753+ EV_CALLSETUP_OUTGOING);
1754+}
1755+
1756+static void handle_call_status(DBusMessage *msg, const char *call_path)
1757+{
1758+ struct csd_call *call;
1759+ dbus_uint32_t status, cause_type, cause;
1760+ int callheld = telephony_get_indicator(maemo_indicators, "callheld");
1761+
1762+ if (!dbus_message_get_args(msg, NULL,
1763+ DBUS_TYPE_UINT32, &status,
1764+ DBUS_TYPE_UINT32, &cause_type,
1765+ DBUS_TYPE_UINT32, &cause,
1766+ DBUS_TYPE_INVALID)) {
1767+ error("Unexpected paramters in Instance.CallStatus() signal");
1768+ return;
1769+ }
1770+
1771+ call = find_call(call_path);
1772+ if (!call) {
1773+ error("Didn't find any matching call object for %s",
1774+ call_path);
1775+ return;
1776+ }
1777+
1778+ if (status > 16) {
1779+ error("Invalid call status %u", status);
1780+ return;
1781+ }
1782+
1783+ DBG("Call %s changed from %s to %s", call_path,
1784+ call_status_str[call->status], call_status_str[status]);
1785+
1786+ if (call->status == (int) status) {
1787+ DBG("Ignoring CSD Call state change to existing state");
1788+ return;
1789+ }
1790+
1791+ call->status = (int) status;
1792+
1793+ switch (status) {
1794+ case CSD_CALL_STATUS_IDLE:
1795+ if (call->setup) {
1796+ telephony_update_indicator(maemo_indicators,
1797+ "callsetup",
1798+ EV_CALLSETUP_INACTIVE);
1799+ if (!call->originating)
1800+ telephony_calling_stopped_ind();
1801+ }
1802+
1803+ g_free(call->number);
1804+ call->number = NULL;
1805+ call->originating = FALSE;
1806+ call->emergency = FALSE;
1807+ call->on_hold = FALSE;
1808+ call->conference = FALSE;
1809+ call->setup = FALSE;
1810+ break;
1811+ case CSD_CALL_STATUS_CREATE:
1812+ call->originating = TRUE;
1813+ call->setup = TRUE;
1814+ break;
1815+ case CSD_CALL_STATUS_COMING:
1816+ call->originating = FALSE;
1817+ call->setup = TRUE;
1818+ break;
1819+ case CSD_CALL_STATUS_PROCEEDING:
1820+ break;
1821+ case CSD_CALL_STATUS_MO_ALERTING:
1822+ telephony_update_indicator(maemo_indicators, "callsetup",
1823+ EV_CALLSETUP_ALERTING);
1824+ break;
1825+ case CSD_CALL_STATUS_MT_ALERTING:
1826+ break;
1827+ case CSD_CALL_STATUS_WAITING:
1828+ break;
1829+ case CSD_CALL_STATUS_ANSWERED:
1830+ break;
1831+ case CSD_CALL_STATUS_ACTIVE:
1832+ if (call->on_hold) {
1833+ call->on_hold = FALSE;
1834+ if (find_call_with_status(CSD_CALL_STATUS_HOLD))
1835+ telephony_update_indicator(maemo_indicators,
1836+ "callheld",
1837+ EV_CALLHELD_MULTIPLE);
1838+ else
1839+ telephony_update_indicator(maemo_indicators,
1840+ "callheld",
1841+ EV_CALLHELD_NONE);
1842+ } else {
1843+ if (!g_slist_find(active_calls, call))
1844+ active_calls = g_slist_prepend(active_calls, call);
1845+ if (g_slist_length(active_calls) == 1)
1846+ telephony_update_indicator(maemo_indicators,
1847+ "call",
1848+ EV_CALL_ACTIVE);
1849+ /* Upgrade callheld status if necessary */
1850+ if (callheld == EV_CALLHELD_ON_HOLD)
1851+ telephony_update_indicator(maemo_indicators,
1852+ "callheld",
1853+ EV_CALLHELD_MULTIPLE);
1854+ telephony_update_indicator(maemo_indicators,
1855+ "callsetup",
1856+ EV_CALLSETUP_INACTIVE);
1857+ if (!call->originating)
1858+ telephony_calling_stopped_ind();
1859+ call->setup = FALSE;
1860+ }
1861+ break;
1862+ case CSD_CALL_STATUS_MO_RELEASE:
1863+ case CSD_CALL_STATUS_MT_RELEASE:
1864+ active_calls = g_slist_remove(active_calls, call);
1865+ if (g_slist_length(active_calls) == 0)
1866+ telephony_update_indicator(maemo_indicators, "call",
1867+ EV_CALL_INACTIVE);
1868+ break;
1869+ case CSD_CALL_STATUS_HOLD_INITIATED:
1870+ break;
1871+ case CSD_CALL_STATUS_HOLD:
1872+ call->on_hold = TRUE;
1873+ if (find_non_held_call())
1874+ telephony_update_indicator(maemo_indicators,
1875+ "callheld",
1876+ EV_CALLHELD_MULTIPLE);
1877+ else
1878+ telephony_update_indicator(maemo_indicators,
1879+ "callheld",
1880+ EV_CALLHELD_ON_HOLD);
1881+ break;
1882+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1883+ break;
1884+ case CSD_CALL_STATUS_RECONNECT_PENDING:
1885+ break;
1886+ case CSD_CALL_STATUS_TERMINATED:
1887+ if (call->on_hold &&
1888+ !find_call_with_status(CSD_CALL_STATUS_HOLD))
1889+ telephony_update_indicator(maemo_indicators,
1890+ "callheld",
1891+ EV_CALLHELD_NONE);
1892+ else if (callheld == EV_CALLHELD_MULTIPLE &&
1893+ find_call_with_status(CSD_CALL_STATUS_HOLD))
1894+ telephony_update_indicator(maemo_indicators,
1895+ "callheld",
1896+ EV_CALLHELD_ON_HOLD);
1897+ break;
1898+ case CSD_CALL_STATUS_SWAP_INITIATED:
1899+ break;
1900+ default:
1901+ error("Unknown call status %u", status);
1902+ break;
1903+ }
1904+}
1905+
1906+static void handle_conference(DBusMessage *msg, gboolean joined)
1907+{
1908+ const char *path;
1909+ struct csd_call *call;
1910+
1911+ if (!dbus_message_get_args(msg, NULL,
1912+ DBUS_TYPE_OBJECT_PATH, &path,
1913+ DBUS_TYPE_INVALID)) {
1914+ error("Unexpected parameters in Conference.%s",
1915+ dbus_message_get_member(msg));
1916+ return;
1917+ }
1918+
1919+ call = find_call(path);
1920+ if (!call) {
1921+ error("Conference signal for unknown call %s", path);
1922+ return;
1923+ }
1924+
1925+ DBG("Call %s %s the conference", path, joined ? "joined" : "left");
1926+
1927+ call->conference = joined;
1928+}
1929+
1930+static void get_operator_name_reply(DBusPendingCall *pending_call,
1931+ void *user_data)
1932+{
1933+ DBusMessage *reply;
1934+ DBusError err;
1935+ const char *name;
1936+ dbus_int32_t net_err;
1937+
1938+ reply = dbus_pending_call_steal_reply(pending_call);
1939+
1940+ dbus_error_init(&err);
1941+ if (dbus_set_error_from_message(&err, reply)) {
1942+ error("get_operator_name failed: %s, %s",
1943+ err.name, err.message);
1944+ dbus_error_free(&err);
1945+ goto done;
1946+ }
1947+
1948+ dbus_error_init(&err);
1949+ if (!dbus_message_get_args(reply, &err,
1950+ DBUS_TYPE_STRING, &name,
1951+ DBUS_TYPE_INT32, &net_err,
1952+ DBUS_TYPE_INVALID)) {
1953+ error("Unexpected get_operator_name reply parameters: %s, %s",
1954+ err.name, err.message);
1955+ dbus_error_free(&err);
1956+ goto done;
1957+ }
1958+
1959+ if (net_err != 0) {
1960+ error("get_operator_name failed with code %d", net_err);
1961+ goto done;
1962+ }
1963+
1964+ if (strlen(name) == 0)
1965+ goto done;
1966+
1967+ g_free(net.operator_name);
1968+ net.operator_name = g_strdup(name);
1969+
1970+ DBG("telephony-maemo: operator name updated: %s", name);
1971+
1972+done:
1973+ dbus_message_unref(reply);
1974+}
1975+
1976+static void resolve_operator_name(uint32_t operator, uint32_t country)
1977+{
1978+ uint8_t name_type = NETWORK_HARDCODED_LATIN_OPER_NAME;
1979+
1980+ send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
1981+ NETWORK_INTERFACE, "get_operator_name",
1982+ get_operator_name_reply, NULL,
1983+ DBUS_TYPE_BYTE, &name_type,
1984+ DBUS_TYPE_UINT32, &operator,
1985+ DBUS_TYPE_UINT32, &country,
1986+ DBUS_TYPE_INVALID);
1987+}
1988+
1989+static void update_registration_status(uint8_t status, uint16_t lac,
1990+ uint32_t cell_id,
1991+ uint32_t operator_code,
1992+ uint32_t country_code,
1993+ uint8_t network_type,
1994+ uint8_t supported_services)
1995+{
1996+ if (net.status != status) {
1997+ switch (status) {
1998+ case NETWORK_REG_STATUS_HOME:
1999+ telephony_update_indicator(maemo_indicators, "roam",
2000+ EV_ROAM_INACTIVE);
2001+ if (net.status >= NETWORK_REG_STATUS_NOSERV)
2002+ telephony_update_indicator(maemo_indicators,
2003+ "service",
2004+ EV_SERVICE_PRESENT);
2005+ break;
2006+ case NETWORK_REG_STATUS_ROAM:
2007+ case NETWORK_REG_STATUS_ROAM_BLINK:
2008+ telephony_update_indicator(maemo_indicators, "roam",
2009+ EV_ROAM_ACTIVE);
2010+ if (net.status >= NETWORK_REG_STATUS_NOSERV)
2011+ telephony_update_indicator(maemo_indicators,
2012+ "service",
2013+ EV_SERVICE_PRESENT);
2014+ break;
2015+ case NETWORK_REG_STATUS_NOSERV:
2016+ case NETWORK_REG_STATUS_NOSERV_SEARCHING:
2017+ case NETWORK_REG_STATUS_NOSERV_NOTSEARCHING:
2018+ case NETWORK_REG_STATUS_NOSERV_NOSIM:
2019+ case NETWORK_REG_STATUS_POWER_OFF:
2020+ case NETWORK_REG_STATUS_NSPS:
2021+ case NETWORK_REG_STATUS_NSPS_NO_COVERAGE:
2022+ case NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW:
2023+ if (net.status < NETWORK_REG_STATUS_NOSERV)
2024+ telephony_update_indicator(maemo_indicators,
2025+ "service",
2026+ EV_SERVICE_NONE);
2027+ break;
2028+ }
2029+
2030+ net.status = status;
2031+ }
2032+
2033+ net.lac = lac;
2034+ net.cell_id = cell_id;
2035+
2036+ if (net.operator_code != operator_code ||
2037+ net.country_code != country_code) {
2038+ g_free(net.operator_name);
2039+ net.operator_name = NULL;
2040+ resolve_operator_name(operator_code, country_code);
2041+ net.operator_code = operator_code;
2042+ net.country_code = country_code;
2043+ }
2044+
2045+ net.network_type = network_type;
2046+ net.supported_services = supported_services;
2047+}
2048+
2049+static void handle_registration_status_change(DBusMessage *msg)
2050+{
2051+ uint8_t status;
2052+ dbus_uint16_t lac, network_type, supported_services;
2053+ dbus_uint32_t cell_id, operator_code, country_code;
2054+
2055+ if (!dbus_message_get_args(msg, NULL,
2056+ DBUS_TYPE_BYTE, &status,
2057+ DBUS_TYPE_UINT16, &lac,
2058+ DBUS_TYPE_UINT32, &cell_id,
2059+ DBUS_TYPE_UINT32, &operator_code,
2060+ DBUS_TYPE_UINT32, &country_code,
2061+ DBUS_TYPE_BYTE, &network_type,
2062+ DBUS_TYPE_BYTE, &supported_services,
2063+ DBUS_TYPE_INVALID)) {
2064+ error("Unexpected parameters in registration_status_change");
2065+ return;
2066+ }
2067+
2068+ update_registration_status(status, lac, cell_id, operator_code,
2069+ country_code, network_type,
2070+ supported_services);
2071+}
2072+
2073+static void update_signal_strength(uint8_t signals_bar)
2074+{
2075+ int signal;
2076+
2077+ if (signals_bar > 100) {
2078+ DBG("signals_bar greater than expected: %u", signals_bar);
2079+ signals_bar = 100;
2080+ }
2081+
2082+ if (net.signals_bar == signals_bar)
2083+ return;
2084+
2085+ /* A simple conversion from 0-100 to 0-5 (used by HFP) */
2086+ signal = (signals_bar + 20) / 21;
2087+
2088+ telephony_update_indicator(maemo_indicators, "signal", signal);
2089+
2090+ net.signals_bar = signals_bar;
2091+
2092+ DBG("Signal strength updated: %u/100, %d/5", signals_bar, signal);
2093+}
2094+
2095+static void handle_signal_strength_change(DBusMessage *msg)
2096+{
2097+ uint8_t signals_bar, rssi_in_dbm;
2098+
2099+ if (!dbus_message_get_args(msg, NULL,
2100+ DBUS_TYPE_BYTE, &signals_bar,
2101+ DBUS_TYPE_BYTE, &rssi_in_dbm,
2102+ DBUS_TYPE_INVALID)) {
2103+ error("Unexpected parameters in signal_strength_change");
2104+ return;
2105+ }
2106+
2107+ update_signal_strength(signals_bar);
2108+}
2109+
2110+static gboolean iter_get_basic_args(DBusMessageIter *iter,
2111+ int first_arg_type, ...)
2112+{
2113+ int type;
2114+ va_list ap;
2115+
2116+ va_start(ap, first_arg_type);
2117+
2118+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
2119+ type = va_arg(ap, int)) {
2120+ void *value = va_arg(ap, void *);
2121+ int real_type = dbus_message_iter_get_arg_type(iter);
2122+
2123+ if (real_type != type) {
2124+ error("iter_get_basic_args: expected %c but got %c",
2125+ (char) type, (char) real_type);
2126+ break;
2127+ }
2128+
2129+ dbus_message_iter_get_basic(iter, value);
2130+ dbus_message_iter_next(iter);
2131+ }
2132+
2133+ va_end(ap);
2134+
2135+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
2136+}
2137+
2138+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
2139+{
2140+ DBusError err;
2141+ DBusMessage *reply;
2142+ dbus_int32_t level;
2143+ int *value = user_data;
2144+
2145+ reply = dbus_pending_call_steal_reply(call);
2146+
2147+ dbus_error_init(&err);
2148+ if (dbus_set_error_from_message(&err, reply)) {
2149+ error("hald replied with an error: %s, %s",
2150+ err.name, err.message);
2151+ dbus_error_free(&err);
2152+ goto done;
2153+ }
2154+
2155+ dbus_error_init(&err);
2156+ if (dbus_message_get_args(reply, &err,
2157+ DBUS_TYPE_INT32, &level,
2158+ DBUS_TYPE_INVALID) == FALSE) {
2159+ error("Unable to parse GetPropertyInteger reply: %s, %s",
2160+ err.name, err.message);
2161+ dbus_error_free(&err);
2162+ goto done;
2163+ }
2164+
2165+ *value = (int) level;
2166+
2167+ if (value == &battchg_last)
2168+ DBG("telephony-maemo: battery.charge_level.last_full is %d",
2169+ *value);
2170+ else if (value == &battchg_design)
2171+ DBG("telephony-maemo: battery.charge_level.design is %d",
2172+ *value);
2173+ else
2174+ DBG("telephony-maemo: battery.charge_level.current is %d",
2175+ *value);
2176+
2177+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
2178+ int new, max;
2179+
2180+ if (battchg_last > 0)
2181+ max = battchg_last;
2182+ else
2183+ max = battchg_design;
2184+
2185+ new = battchg_cur * 5 / max;
2186+
2187+ telephony_update_indicator(maemo_indicators, "battchg", new);
2188+ }
2189+done:
2190+ dbus_message_unref(reply);
2191+}
2192+
2193+static void hal_get_integer(const char *path, const char *key, void *user_data)
2194+{
2195+ send_method_call("org.freedesktop.Hal", path,
2196+ "org.freedesktop.Hal.Device",
2197+ "GetPropertyInteger",
2198+ hal_battery_level_reply, user_data,
2199+ DBUS_TYPE_STRING, &key,
2200+ DBUS_TYPE_INVALID);
2201+}
2202+
2203+static void handle_hal_property_modified(DBusMessage *msg)
2204+{
2205+ DBusMessageIter iter, array;
2206+ dbus_int32_t num_changes;
2207+ const char *path;
2208+
2209+ path = dbus_message_get_path(msg);
2210+
2211+ dbus_message_iter_init(msg, &iter);
2212+
2213+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
2214+ error("Unexpected signature in hal PropertyModified signal");
2215+ return;
2216+ }
2217+
2218+ dbus_message_iter_get_basic(&iter, &num_changes);
2219+ dbus_message_iter_next(&iter);
2220+
2221+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2222+ error("Unexpected signature in hal PropertyModified signal");
2223+ return;
2224+ }
2225+
2226+ dbus_message_iter_recurse(&iter, &array);
2227+
2228+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
2229+ DBusMessageIter prop;
2230+ const char *name;
2231+ dbus_bool_t added, removed;
2232+
2233+ dbus_message_iter_recurse(&array, &prop);
2234+
2235+ if (!iter_get_basic_args(&prop,
2236+ DBUS_TYPE_STRING, &name,
2237+ DBUS_TYPE_BOOLEAN, &added,
2238+ DBUS_TYPE_BOOLEAN, &removed,
2239+ DBUS_TYPE_INVALID)) {
2240+ error("Invalid hal PropertyModified parameters");
2241+ break;
2242+ }
2243+
2244+ if (g_str_equal(name, "battery.charge_level.last_full"))
2245+ hal_get_integer(path, name, &battchg_last);
2246+ else if (g_str_equal(name, "battery.charge_level.current"))
2247+ hal_get_integer(path, name, &battchg_cur);
2248+ else if (g_str_equal(name, "battery.charge_level.design"))
2249+ hal_get_integer(path, name, &battchg_design);
2250+
2251+ dbus_message_iter_next(&array);
2252+ }
2253+}
2254+
2255+static void csd_call_free(struct csd_call *call)
2256+{
2257+ if (!call)
2258+ return;
2259+
2260+ g_free(call->object_path);
2261+ g_free(call->number);
2262+
2263+ g_free(call);
2264+}
2265+
2266+static void parse_call_list(DBusMessageIter *iter)
2267+{
2268+ do {
2269+ DBusMessageIter call_iter;
2270+ struct csd_call *call;
2271+ const char *object_path, *number;
2272+ dbus_uint32_t status;
2273+ dbus_bool_t originating, terminating, emerg, on_hold, conf;
2274+
2275+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
2276+ error("Unexpected signature in GetCallInfoAll reply");
2277+ break;
2278+ }
2279+
2280+ dbus_message_iter_recurse(iter, &call_iter);
2281+
2282+ if (!iter_get_basic_args(&call_iter,
2283+ DBUS_TYPE_OBJECT_PATH, &object_path,
2284+ DBUS_TYPE_UINT32, &status,
2285+ DBUS_TYPE_BOOLEAN, &originating,
2286+ DBUS_TYPE_BOOLEAN, &terminating,
2287+ DBUS_TYPE_BOOLEAN, &emerg,
2288+ DBUS_TYPE_BOOLEAN, &on_hold,
2289+ DBUS_TYPE_BOOLEAN, &conf,
2290+ DBUS_TYPE_STRING, &number,
2291+ DBUS_TYPE_INVALID)) {
2292+ error("Parsing call D-Bus parameters failed");
2293+ break;
2294+ }
2295+
2296+ call = find_call(object_path);
2297+ if (!call) {
2298+ call = g_new0(struct csd_call, 1);
2299+ call->object_path = g_strdup(object_path);
2300+ call->status = (int) status;
2301+ calls = g_slist_append(calls, call);
2302+ DBG("telephony-maemo: new csd call instance at %s",
2303+ object_path);
2304+ }
2305+
2306+ if (call->status == CSD_CALL_STATUS_IDLE)
2307+ continue;
2308+
2309+ /* CSD gives incorrect call_hold property sometimes */
2310+ if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
2311+ (call->status == CSD_CALL_STATUS_HOLD &&
2312+ !on_hold)) {
2313+ error("Conflicting call status and on_hold property!");
2314+ on_hold = call->status == CSD_CALL_STATUS_HOLD;
2315+ }
2316+
2317+ call->originating = originating;
2318+ call->on_hold = on_hold;
2319+ call->conference = conf;
2320+ g_free(call->number);
2321+ call->number = g_strdup(number);
2322+
2323+ } while (dbus_message_iter_next(iter));
2324+}
2325+
2326+static void signal_strength_reply(DBusPendingCall *call, void *user_data)
2327+{
2328+ DBusError err;
2329+ DBusMessage *reply;
2330+ uint8_t signals_bar, rssi_in_dbm;
2331+ dbus_int32_t net_err;
2332+
2333+ reply = dbus_pending_call_steal_reply(call);
2334+
2335+ dbus_error_init(&err);
2336+ if (dbus_set_error_from_message(&err, reply)) {
2337+ error("Unable to get signal strength: %s, %s",
2338+ err.name, err.message);
2339+ dbus_error_free(&err);
2340+ goto done;
2341+ }
2342+
2343+ dbus_error_init(&err);
2344+ if (!dbus_message_get_args(reply, &err,
2345+ DBUS_TYPE_BYTE, &signals_bar,
2346+ DBUS_TYPE_BYTE, &rssi_in_dbm,
2347+ DBUS_TYPE_INT32, &net_err,
2348+ DBUS_TYPE_INVALID)) {
2349+ error("Unable to parse signal_strength reply: %s, %s",
2350+ err.name, err.message);
2351+ dbus_error_free(&err);
2352+ goto done;
2353+ }
2354+
2355+ if (net_err != 0) {
2356+ error("get_signal_strength failed with code %d", net_err);
2357+ goto done;
2358+ }
2359+
2360+ update_signal_strength(signals_bar);
2361+
2362+done:
2363+ dbus_message_unref(reply);
2364+}
2365+
2366+static int get_signal_strength(void)
2367+{
2368+ return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
2369+ NETWORK_INTERFACE, "get_signal_strength",
2370+ signal_strength_reply, NULL,
2371+ DBUS_TYPE_INVALID);
2372+}
2373+
2374+static void registration_status_reply(DBusPendingCall *call, void *user_data)
2375+{
2376+ DBusError err;
2377+ DBusMessage *reply;
2378+ uint8_t status;
2379+ dbus_uint16_t lac, network_type, supported_services;
2380+ dbus_uint32_t cell_id, operator_code, country_code;
2381+ dbus_int32_t net_err;
2382+
2383+ reply = dbus_pending_call_steal_reply(call);
2384+
2385+ dbus_error_init(&err);
2386+ if (dbus_set_error_from_message(&err, reply)) {
2387+ error("Unable to get registration status: %s, %s",
2388+ err.name, err.message);
2389+ dbus_error_free(&err);
2390+ goto done;
2391+ }
2392+
2393+ dbus_error_init(&err);
2394+ if (!dbus_message_get_args(reply, &err,
2395+ DBUS_TYPE_BYTE, &status,
2396+ DBUS_TYPE_UINT16, &lac,
2397+ DBUS_TYPE_UINT32, &cell_id,
2398+ DBUS_TYPE_UINT32, &operator_code,
2399+ DBUS_TYPE_UINT32, &country_code,
2400+ DBUS_TYPE_BYTE, &network_type,
2401+ DBUS_TYPE_BYTE, &supported_services,
2402+ DBUS_TYPE_INT32, &net_err,
2403+ DBUS_TYPE_INVALID)) {
2404+ error("Unable to parse registration_status_change reply:"
2405+ " %s, %s", err.name, err.message);
2406+ dbus_error_free(&err);
2407+ goto done;
2408+ }
2409+
2410+ if (net_err != 0) {
2411+ error("get_registration_status failed with code %d", net_err);
2412+ goto done;
2413+ }
2414+
2415+ update_registration_status(status, lac, cell_id, operator_code,
2416+ country_code, network_type,
2417+ supported_services);
2418+
2419+ get_signal_strength();
2420+
2421+done:
2422+ dbus_message_unref(reply);
2423+}
2424+
2425+static int get_registration_status(void)
2426+{
2427+ return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
2428+ NETWORK_INTERFACE, "get_registration_status",
2429+ registration_status_reply, NULL,
2430+ DBUS_TYPE_INVALID);
2431+}
2432+
2433+static void call_info_reply(DBusPendingCall *call, void *user_data)
2434+{
2435+ DBusError err;
2436+ DBusMessage *reply;
2437+ DBusMessageIter iter, sub;
2438+
2439+ get_calls_active = FALSE;
2440+
2441+ reply = dbus_pending_call_steal_reply(call);
2442+
2443+ dbus_error_init(&err);
2444+ if (dbus_set_error_from_message(&err, reply)) {
2445+ error("csd replied with an error: %s, %s",
2446+ err.name, err.message);
2447+ dbus_error_free(&err);
2448+ goto done;
2449+ }
2450+
2451+ dbus_message_iter_init(reply, &iter);
2452+
2453+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2454+ error("Unexpected signature in GetCallInfoAll return");
2455+ goto done;
2456+ }
2457+
2458+ dbus_message_iter_recurse(&iter, &sub);
2459+
2460+ parse_call_list(&sub);
2461+
2462+ get_registration_status();
2463+
2464+done:
2465+ dbus_message_unref(reply);
2466+}
2467+
2468+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
2469+{
2470+ DBusError err;
2471+ DBusMessage *reply;
2472+ DBusMessageIter iter, sub;
2473+ const char *path;
2474+ char match_string[256];
2475+ int type;
2476+
2477+ reply = dbus_pending_call_steal_reply(call);
2478+
2479+ dbus_error_init(&err);
2480+ if (dbus_set_error_from_message(&err, reply)) {
2481+ error("hald replied with an error: %s, %s",
2482+ err.name, err.message);
2483+ dbus_error_free(&err);
2484+ goto done;
2485+ }
2486+
2487+ dbus_message_iter_init(reply, &iter);
2488+
2489+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2490+ error("Unexpected signature in FindDeviceByCapability return");
2491+ goto done;
2492+ }
2493+
2494+ dbus_message_iter_recurse(&iter, &sub);
2495+
2496+ type = dbus_message_iter_get_arg_type(&sub);
2497+
2498+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
2499+ error("No hal device with battery capability found");
2500+ goto done;
2501+ }
2502+
2503+ dbus_message_iter_get_basic(&sub, &path);
2504+
2505+ DBG("telephony-maemo: found battery device at %s", path);
2506+
2507+ snprintf(match_string, sizeof(match_string),
2508+ "type='signal',"
2509+ "path='%s',"
2510+ "interface='org.freedesktop.Hal.Device',"
2511+ "member='PropertyModified'", path);
2512+ dbus_bus_add_match(connection, match_string, NULL);
2513+
2514+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
2515+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
2516+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
2517+
2518+done:
2519+ dbus_message_unref(reply);
2520+}
2521+
2522+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
2523+{
2524+ DBusError derr;
2525+ DBusMessage *reply;
2526+ const char *name, *number;
2527+ char **number_type = user_data;
2528+ dbus_int32_t current_location, err;
2529+
2530+ reply = dbus_pending_call_steal_reply(call);
2531+
2532+ dbus_error_init(&derr);
2533+ if (dbus_set_error_from_message(&derr, reply)) {
2534+ error("SIM.Phonebook replied with an error: %s, %s",
2535+ derr.name, derr.message);
2536+ dbus_error_free(&derr);
2537+ goto done;
2538+ }
2539+
2540+ dbus_error_init(&derr);
2541+ if (dbus_message_get_args(reply, &derr,
2542+ DBUS_TYPE_STRING, &name,
2543+ DBUS_TYPE_STRING, &number,
2544+ DBUS_TYPE_INT32, &current_location,
2545+ DBUS_TYPE_INT32, &err,
2546+ DBUS_TYPE_INVALID) == FALSE) {
2547+ error("Unable to parse SIM.Phonebook.read arguments: %s, %s",
2548+ derr.name, derr.message);
2549+ dbus_error_free(&derr);
2550+ goto done;
2551+ }
2552+
2553+ if (err != 0) {
2554+ error("SIM.Phonebook.read failed with error %d", err);
2555+ if (number_type == &vmbx)
2556+ vmbx = g_strdup(getenv("VMBX_NUMBER"));
2557+ goto done;
2558+ }
2559+
2560+ if (number_type == &msisdn) {
2561+ g_free(msisdn);
2562+ msisdn = g_strdup(number);
2563+ DBG("Got MSISDN %s (%s)", number, name);
2564+ } else {
2565+ g_free(vmbx);
2566+ vmbx = g_strdup(number);
2567+ DBG("Got voice mailbox number %s (%s)", number, name);
2568+ }
2569+
2570+done:
2571+ dbus_message_unref(reply);
2572+}
2573+
2574+static void csd_init(void)
2575+{
2576+ dbus_uint32_t location;
2577+ uint8_t pb_type, location_type;
2578+ int ret;
2579+
2580+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
2581+ CSD_CALL_INTERFACE, "GetCallInfoAll",
2582+ call_info_reply, NULL, DBUS_TYPE_INVALID);
2583+ if (ret < 0) {
2584+ error("Unable to sent GetCallInfoAll method call");
2585+ return;
2586+ }
2587+
2588+ get_calls_active = TRUE;
2589+
2590+ pb_type = SIM_PHONEBOOK_TYPE_MSISDN;
2591+ location = PHONEBOOK_INDEX_FIRST_ENTRY;
2592+ location_type = SIM_PHONEBOOK_LOCATION_NEXT;
2593+
2594+ ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
2595+ SIM_PHONEBOOK_INTERFACE, "read",
2596+ phonebook_read_reply, &msisdn,
2597+ DBUS_TYPE_BYTE, &pb_type,
2598+ DBUS_TYPE_INT32, &location,
2599+ DBUS_TYPE_BYTE, &location_type,
2600+ DBUS_TYPE_INVALID);
2601+ if (ret < 0) {
2602+ error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
2603+ return;
2604+ }
2605+
2606+ pb_type = SIM_PHONEBOOK_TYPE_MBDN;
2607+ location = PHONEBOOK_INDEX_FIRST_ENTRY;
2608+ location_type = SIM_PHONEBOOK_LOCATION_NEXT;
2609+
2610+ ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
2611+ SIM_PHONEBOOK_INTERFACE, "read",
2612+ phonebook_read_reply, &vmbx,
2613+ DBUS_TYPE_BYTE, &pb_type,
2614+ DBUS_TYPE_INT32, &location,
2615+ DBUS_TYPE_BYTE, &location_type,
2616+ DBUS_TYPE_INVALID);
2617+ if (ret < 0) {
2618+ error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
2619+ return;
2620+ }
2621+}
2622+
2623+static uint32_t get_callflag(const char *callerid_setting)
2624+{
2625+ if (callerid_setting != NULL) {
2626+ if (g_str_equal(callerid_setting, "allowed"))
2627+ return CALL_FLAG_PRESENTATION_ALLOWED;
2628+ else if (g_str_equal(callerid_setting, "restricted"))
2629+ return CALL_FLAG_PRESENTATION_RESTRICTED;
2630+ else
2631+ return CALL_FLAG_NONE;
2632+ } else
2633+ return CALL_FLAG_NONE;
2634+}
2635+
2636+static void generate_flag_file(const char *filename)
2637+{
2638+ int fd;
2639+
2640+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
2641+ g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
2642+ g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
2643+ return;
2644+
2645+ fd = open(filename, O_WRONLY | O_CREAT, 0);
2646+ if (fd >= 0)
2647+ close(fd);
2648+}
2649+
2650+static void save_callerid_to_file(const char *callerid_setting)
2651+{
2652+ char callerid_file[FILENAME_MAX];
2653+
2654+ snprintf(callerid_file, sizeof(callerid_file), "%s%s",
2655+ CALLERID_BASE, callerid_setting);
2656+
2657+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
2658+ rename(ALLOWED_FLAG_FILE, callerid_file);
2659+ else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
2660+ rename(RESTRICTED_FLAG_FILE, callerid_file);
2661+ else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
2662+ rename(NONE_FLAG_FILE, callerid_file);
2663+ else
2664+ generate_flag_file(callerid_file);
2665+}
2666+
2667+static uint32_t callerid_from_file(void)
2668+{
2669+ if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
2670+ return CALL_FLAG_PRESENTATION_ALLOWED;
2671+ else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
2672+ return CALL_FLAG_PRESENTATION_RESTRICTED;
2673+ else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
2674+ return CALL_FLAG_NONE;
2675+ else
2676+ return CALL_FLAG_NONE;
2677+}
2678+
2679+static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg,
2680+ void *data)
2681+{
2682+ const char *callerid_setting;
2683+
2684+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
2685+ &callerid_setting,
2686+ DBUS_TYPE_INVALID) == FALSE)
2687+ return btd_error_invalid_args(msg);
2688+
2689+ if (g_str_equal(callerid_setting, "allowed") ||
2690+ g_str_equal(callerid_setting, "restricted") ||
2691+ g_str_equal(callerid_setting, "none")) {
2692+ save_callerid_to_file(callerid_setting);
2693+ callerid = get_callflag(callerid_setting);
2694+ DBG("telephony-maemo setting callerid flag: %s",
2695+ callerid_setting);
2696+ return dbus_message_new_method_return(msg);
2697+ }
2698+
2699+ error("telephony-maemo: invalid argument %s for method call"
2700+ " SetCallerId", callerid_setting);
2701+ return btd_error_invalid_args(msg);
2702+}
2703+
2704+static GDBusMethodTable telephony_maemo_methods[] = {
2705+ {"SetCallerId", "s", "", set_callerid,
2706+ G_DBUS_METHOD_FLAG_ASYNC},
2707+ { }
2708+};
2709+
2710+static void handle_modem_state(DBusMessage *msg)
2711+{
2712+ const char *state;
2713+
2714+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
2715+ DBUS_TYPE_INVALID)) {
2716+ error("Unexpected modem state parameters");
2717+ return;
2718+ }
2719+
2720+ DBG("SSC modem state: %s", state);
2721+
2722+ if (calls != NULL || get_calls_active)
2723+ return;
2724+
2725+ if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
2726+ csd_init();
2727+}
2728+
2729+static void modem_state_reply(DBusPendingCall *call, void *user_data)
2730+{
2731+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
2732+ DBusError err;
2733+
2734+ dbus_error_init(&err);
2735+ if (dbus_set_error_from_message(&err, reply)) {
2736+ error("get_modem_status: %s, %s", err.name, err.message);
2737+ dbus_error_free(&err);
2738+ } else
2739+ handle_modem_state(reply);
2740+
2741+ dbus_message_unref(reply);
2742+}
2743+
2744+static DBusHandlerResult signal_filter(DBusConnection *conn,
2745+ DBusMessage *msg, void *data)
2746+{
2747+ const char *path = dbus_message_get_path(msg);
2748+
2749+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
2750+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2751+
2752+ if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
2753+ handle_incoming_call(msg);
2754+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
2755+ handle_outgoing_call(msg);
2756+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
2757+ "CreateRequested"))
2758+ handle_create_requested(msg);
2759+ else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
2760+ handle_call_status(msg, path);
2761+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
2762+ handle_conference(msg, TRUE);
2763+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
2764+ handle_conference(msg, FALSE);
2765+ else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
2766+ "registration_status_change"))
2767+ handle_registration_status_change(msg);
2768+ else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
2769+ "signal_strength_change"))
2770+ handle_signal_strength_change(msg);
2771+ else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
2772+ "PropertyModified"))
2773+ handle_hal_property_modified(msg);
2774+ else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
2775+ "modem_state_changed_ind"))
2776+ handle_modem_state(msg);
2777+
2778+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2779+}
2780+
2781+int telephony_init(void)
2782+{
2783+ const char *battery_cap = "battery";
2784+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
2785+ AG_FEATURE_INBAND_RINGTONE |
2786+ AG_FEATURE_REJECT_A_CALL |
2787+ AG_FEATURE_ENHANCED_CALL_STATUS |
2788+ AG_FEATURE_ENHANCED_CALL_CONTROL |
2789+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
2790+ AG_FEATURE_THREE_WAY_CALLING;
2791+
2792+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
2793+
2794+ if (!dbus_connection_add_filter(connection, signal_filter,
2795+ NULL, NULL))
2796+ error("Can't add signal filter");
2797+
2798+ dbus_bus_add_match(connection,
2799+ "type=signal,interface=" CSD_CALL_INTERFACE, NULL);
2800+ dbus_bus_add_match(connection,
2801+ "type=signal,interface=" CSD_CALL_INSTANCE, NULL);
2802+ dbus_bus_add_match(connection,
2803+ "type=signal,interface=" CSD_CALL_CONFERENCE, NULL);
2804+ dbus_bus_add_match(connection,
2805+ "type=signal,interface=" NETWORK_INTERFACE, NULL);
2806+ dbus_bus_add_match(connection,
2807+ "type=signal,interface=" SSC_DBUS_IFACE
2808+ ",member=modem_state_changed_ind", NULL);
2809+
2810+ if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
2811+ "get_modem_state", modem_state_reply,
2812+ NULL, DBUS_TYPE_INVALID) < 0)
2813+ error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
2814+
2815+ generate_flag_file(NONE_FLAG_FILE);
2816+ callerid = callerid_from_file();
2817+
2818+ if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH,
2819+ TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods,
2820+ NULL, NULL, NULL, NULL)) {
2821+ error("telephony-maemo interface %s init failed on path %s",
2822+ TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
2823+ }
2824+
2825+ DBG("telephony-maemo registering %s interface on path %s",
2826+ TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
2827+
2828+ telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
2829+ chld_str);
2830+ if (send_method_call("org.freedesktop.Hal",
2831+ "/org/freedesktop/Hal/Manager",
2832+ "org.freedesktop.Hal.Manager",
2833+ "FindDeviceByCapability",
2834+ hal_find_device_reply, NULL,
2835+ DBUS_TYPE_STRING, &battery_cap,
2836+ DBUS_TYPE_INVALID) < 0)
2837+ error("Unable to send HAL method call");
2838+
2839+ return 0;
2840+}
2841+
2842+void telephony_exit(void)
2843+{
2844+ g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
2845+ g_slist_free(calls);
2846+ calls = NULL;
2847+
2848+ dbus_connection_remove_filter(connection, signal_filter, NULL);
2849+
2850+ dbus_connection_unref(connection);
2851+ connection = NULL;
2852+
2853+ telephony_deinit();
2854+}
2855
2856=== added file '.pc/02_disable_hal.patch/audio/telephony-maemo6.c'
2857--- .pc/02_disable_hal.patch/audio/telephony-maemo6.c 1970-01-01 00:00:00 +0000
2858+++ .pc/02_disable_hal.patch/audio/telephony-maemo6.c 2013-04-19 02:35:36 +0000
2859@@ -0,0 +1,2201 @@
2860+/*
2861+ *
2862+ * BlueZ - Bluetooth protocol stack for Linux
2863+ *
2864+ * Copyright (C) 2008-2010 Nokia Corporation
2865+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
2866+ *
2867+ *
2868+ * This program is free software; you can redistribute it and/or modify
2869+ * it under the terms of the GNU General Public License as published by
2870+ * the Free Software Foundation; either version 2 of the License, or
2871+ * (at your option) any later version.
2872+ *
2873+ * This program is distributed in the hope that it will be useful,
2874+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2875+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2876+ * GNU General Public License for more details.
2877+ *
2878+ * You should have received a copy of the GNU General Public License
2879+ * along with this program; if not, write to the Free Software
2880+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
2881+ *
2882+ */
2883+
2884+#ifdef HAVE_CONFIG_H
2885+#include <config.h>
2886+#endif
2887+
2888+#include <stdlib.h>
2889+#include <stdio.h>
2890+#include <unistd.h>
2891+#include <fcntl.h>
2892+#include <stdint.h>
2893+#include <string.h>
2894+#include <glib.h>
2895+#include <dbus/dbus.h>
2896+#include <gdbus.h>
2897+
2898+#include <bluetooth/sdp.h>
2899+
2900+#include "glib-compat.h"
2901+#include "log.h"
2902+#include "telephony.h"
2903+#include "error.h"
2904+
2905+/* SSC D-Bus definitions */
2906+#define SSC_DBUS_NAME "com.nokia.phone.SSC"
2907+#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
2908+#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
2909+
2910+/* libcsnet D-Bus definitions */
2911+#define CSD_CSNET_BUS_NAME "com.nokia.csd.CSNet"
2912+#define CSD_CSNET_PATH "/com/nokia/csd/csnet"
2913+#define CSD_CSNET_IFACE "com.nokia.csd.CSNet"
2914+#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration"
2915+#define CSD_CSNET_OPERATOR "com.nokia.csd.CSNet.NetworkOperator"
2916+#define CSD_CSNET_SIGNAL "com.nokia.csd.CSNet.SignalStrength"
2917+
2918+enum net_registration_status {
2919+ NETWORK_REG_STATUS_HOME,
2920+ NETWORK_REG_STATUS_ROAMING,
2921+ NETWORK_REG_STATUS_OFFLINE,
2922+ NETWORK_REG_STATUS_SEARCHING,
2923+ NETWORK_REG_STATUS_NO_SIM,
2924+ NETWORK_REG_STATUS_POWEROFF,
2925+ NETWORK_REG_STATUS_POWERSAFE,
2926+ NETWORK_REG_STATUS_NO_COVERAGE,
2927+ NETWORK_REG_STATUS_REJECTED,
2928+ NETWORK_REG_STATUS_UNKOWN
2929+};
2930+
2931+/* CSD CALL plugin D-Bus definitions */
2932+#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
2933+#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
2934+#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
2935+#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
2936+#define CSD_CALL_PATH "/com/nokia/csd/call"
2937+#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
2938+
2939+/* Call status values as exported by the CSD CALL plugin */
2940+#define CSD_CALL_STATUS_IDLE 0
2941+#define CSD_CALL_STATUS_CREATE 1
2942+#define CSD_CALL_STATUS_COMING 2
2943+#define CSD_CALL_STATUS_PROCEEDING 3
2944+#define CSD_CALL_STATUS_MO_ALERTING 4
2945+#define CSD_CALL_STATUS_MT_ALERTING 5
2946+#define CSD_CALL_STATUS_WAITING 6
2947+#define CSD_CALL_STATUS_ANSWERED 7
2948+#define CSD_CALL_STATUS_ACTIVE 8
2949+#define CSD_CALL_STATUS_MO_RELEASE 9
2950+#define CSD_CALL_STATUS_MT_RELEASE 10
2951+#define CSD_CALL_STATUS_HOLD_INITIATED 11
2952+#define CSD_CALL_STATUS_HOLD 12
2953+#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
2954+#define CSD_CALL_STATUS_RECONNECT_PENDING 14
2955+#define CSD_CALL_STATUS_TERMINATED 15
2956+#define CSD_CALL_STATUS_SWAP_INITIATED 16
2957+
2958+#define CALL_FLAG_NONE 0
2959+#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
2960+#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
2961+
2962+/* SIM Phonebook D-Bus definitions */
2963+#define CSD_SIMPB_BUS_NAME "com.nokia.csd.SIM"
2964+#define CSD_SIMPB_INTERFACE "com.nokia.csd.SIM.Phonebook"
2965+#define CSD_SIMPB_PATH "/com/nokia/csd/sim/phonebook"
2966+
2967+#define CSD_SIMPB_TYPE_ADN "ADN"
2968+#define CSD_SIMPB_TYPE_FDN "FDN"
2969+#define CSD_SIMPB_TYPE_SDN "SDN"
2970+#define CSD_SIMPB_TYPE_VMBX "VMBX"
2971+#define CSD_SIMPB_TYPE_MBDN "MBDN"
2972+#define CSD_SIMPB_TYPE_EN "EN"
2973+#define CSD_SIMPB_TYPE_MSISDN "MSISDN"
2974+
2975+/* OHM plugin D-Bus definitions */
2976+#define OHM_BUS_NAME "com.nokia.NonGraphicFeedback1"
2977+#define OHM_INTERFACE "com.nokia.NonGraphicFeedback1"
2978+#define OHM_PATH "/com/nokia/NonGraphicFeedback1"
2979+
2980+/* tone-genenerator D-Bus definitions */
2981+#define TONEGEN_BUS_NAME "com.Nokia.Telephony.Tones"
2982+#define TONEGEN_INTERFACE "com.Nokia.Telephony.Tones"
2983+#define TONEGEN_PATH "/com/Nokia/Telephony/Tones"
2984+
2985+/* tone-generator DTMF definitions */
2986+#define DTMF_ASTERISK 10
2987+#define DTMF_HASHMARK 11
2988+#define DTMF_A 12
2989+#define DTMF_B 13
2990+#define DTMF_C 14
2991+#define DTMF_D 15
2992+
2993+#define FEEDBACK_TONE_DURATION 200
2994+
2995+struct csd_call {
2996+ char *object_path;
2997+ int status;
2998+ gboolean originating;
2999+ gboolean emergency;
3000+ gboolean on_hold;
3001+ gboolean conference;
3002+ char *number;
3003+ gboolean setup;
3004+};
3005+
3006+static struct {
3007+ char *operator_name;
3008+ uint8_t status;
3009+ int32_t signal_bars;
3010+} net = {
3011+ .operator_name = NULL,
3012+ .status = NETWORK_REG_STATUS_UNKOWN,
3013+ /* Init as 0 meaning inactive mode. In modem power off state
3014+ * can be be -1, but we treat all values as 0s regardless
3015+ * inactive or power off. */
3016+ .signal_bars = 0,
3017+};
3018+
3019+struct pending_req {
3020+ DBusPendingCall *call;
3021+ void *user_data;
3022+};
3023+
3024+static int get_property(const char *iface, const char *prop);
3025+
3026+static DBusConnection *connection = NULL;
3027+
3028+static GSList *calls = NULL;
3029+static GSList *watches = NULL;
3030+static GSList *pending = NULL;
3031+
3032+/* Reference count for determining the call indicator status */
3033+static GSList *active_calls = NULL;
3034+
3035+/* Queue of DTMF tones to play */
3036+static GSList *tones = NULL;
3037+static guint create_tones_timer = 0;
3038+
3039+static char *msisdn = NULL; /* Subscriber number */
3040+static char *vmbx = NULL; /* Voice mailbox number */
3041+
3042+/* HAL battery namespace key values */
3043+static int battchg_cur = -1; /* "battery.charge_level.current" */
3044+static int battchg_last = -1; /* "battery.charge_level.last_full" */
3045+static int battchg_design = -1; /* "battery.charge_level.design" */
3046+
3047+static gboolean get_calls_active = FALSE;
3048+
3049+static gboolean events_enabled = FALSE;
3050+
3051+/* Supported set of call hold operations */
3052+static const char *chld_str = "0,1,1x,2,2x,3,4";
3053+
3054+/* Timer for tracking call creation requests */
3055+static guint create_request_timer = 0;
3056+
3057+static struct indicator maemo_indicators[] =
3058+{
3059+ { "battchg", "0-5", 5, TRUE },
3060+ /* signal strength in terms of bars */
3061+ { "signal", "0-5", 0, TRUE },
3062+ { "service", "0,1", 0, TRUE },
3063+ { "call", "0,1", 0, TRUE },
3064+ { "callsetup", "0-3", 0, TRUE },
3065+ { "callheld", "0-2", 0, FALSE },
3066+ { "roam", "0,1", 0, TRUE },
3067+ { NULL }
3068+};
3069+
3070+static char *call_status_str[] = {
3071+ "IDLE",
3072+ "CREATE",
3073+ "COMING",
3074+ "PROCEEDING",
3075+ "MO_ALERTING",
3076+ "MT_ALERTING",
3077+ "WAITING",
3078+ "ANSWERED",
3079+ "ACTIVE",
3080+ "MO_RELEASE",
3081+ "MT_RELEASE",
3082+ "HOLD_INITIATED",
3083+ "HOLD",
3084+ "RETRIEVE_INITIATED",
3085+ "RECONNECT_PENDING",
3086+ "TERMINATED",
3087+ "SWAP_INITIATED",
3088+ "???"
3089+};
3090+
3091+static int send_method_call(const char *dest, const char *path,
3092+ const char *interface, const char *method,
3093+ DBusPendingCallNotifyFunction cb,
3094+ void *user_data, int type, ...)
3095+{
3096+ DBusMessage *msg;
3097+ DBusPendingCall *call;
3098+ va_list args;
3099+ struct pending_req *req;
3100+
3101+ msg = dbus_message_new_method_call(dest, path, interface, method);
3102+ if (!msg) {
3103+ error("Unable to allocate new D-Bus %s message", method);
3104+ return -ENOMEM;
3105+ }
3106+
3107+ va_start(args, type);
3108+
3109+ if (!dbus_message_append_args_valist(msg, type, args)) {
3110+ dbus_message_unref(msg);
3111+ va_end(args);
3112+ return -EIO;
3113+ }
3114+
3115+ va_end(args);
3116+
3117+ if (!cb) {
3118+ g_dbus_send_message(connection, msg);
3119+ return 0;
3120+ }
3121+
3122+ if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
3123+ error("Sending %s failed", method);
3124+ dbus_message_unref(msg);
3125+ return -EIO;
3126+ }
3127+
3128+ dbus_pending_call_set_notify(call, cb, user_data, NULL);
3129+
3130+ req = g_new0(struct pending_req, 1);
3131+ req->call = call;
3132+ req->user_data = user_data;
3133+
3134+ pending = g_slist_prepend(pending, req);
3135+ dbus_message_unref(msg);
3136+
3137+ return 0;
3138+}
3139+
3140+static struct csd_call *find_call(const char *path)
3141+{
3142+ GSList *l;
3143+
3144+ for (l = calls; l != NULL; l = l->next) {
3145+ struct csd_call *call = l->data;
3146+
3147+ if (g_str_equal(call->object_path, path))
3148+ return call;
3149+ }
3150+
3151+ return NULL;
3152+}
3153+
3154+static struct csd_call *find_non_held_call(void)
3155+{
3156+ GSList *l;
3157+
3158+ for (l = calls; l != NULL; l = l->next) {
3159+ struct csd_call *call = l->data;
3160+
3161+ if (call->status == CSD_CALL_STATUS_IDLE)
3162+ continue;
3163+
3164+ if (call->status != CSD_CALL_STATUS_HOLD)
3165+ return call;
3166+ }
3167+
3168+ return NULL;
3169+}
3170+
3171+static struct csd_call *find_non_idle_call(void)
3172+{
3173+ GSList *l;
3174+
3175+ for (l = calls; l != NULL; l = l->next) {
3176+ struct csd_call *call = l->data;
3177+
3178+ if (call->status != CSD_CALL_STATUS_IDLE)
3179+ return call;
3180+ }
3181+
3182+ return NULL;
3183+}
3184+
3185+static struct csd_call *find_call_with_status(int status)
3186+{
3187+ GSList *l;
3188+
3189+ for (l = calls; l != NULL; l = l->next) {
3190+ struct csd_call *call = l->data;
3191+
3192+ if (call->status == status)
3193+ return call;
3194+ }
3195+
3196+ return NULL;
3197+}
3198+
3199+static int release_conference(void)
3200+{
3201+ DBusMessage *msg;
3202+
3203+ DBG("telephony-maemo6: releasing conference call");
3204+
3205+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
3206+ CSD_CALL_CONFERENCE_PATH,
3207+ CSD_CALL_INSTANCE,
3208+ "Release");
3209+ if (!msg) {
3210+ error("Unable to allocate new D-Bus message");
3211+ return -ENOMEM;
3212+ }
3213+
3214+ g_dbus_send_message(connection, msg);
3215+
3216+ return 0;
3217+}
3218+
3219+static int release_call(struct csd_call *call)
3220+{
3221+ DBusMessage *msg;
3222+
3223+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
3224+ call->object_path,
3225+ CSD_CALL_INSTANCE,
3226+ "Release");
3227+ if (!msg) {
3228+ error("Unable to allocate new D-Bus message");
3229+ return -ENOMEM;
3230+ }
3231+
3232+ g_dbus_send_message(connection, msg);
3233+
3234+ return 0;
3235+}
3236+
3237+static int answer_call(struct csd_call *call)
3238+{
3239+ DBusMessage *msg;
3240+
3241+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
3242+ call->object_path,
3243+ CSD_CALL_INSTANCE,
3244+ "Answer");
3245+ if (!msg) {
3246+ error("Unable to allocate new D-Bus message");
3247+ return -ENOMEM;
3248+ }
3249+
3250+ g_dbus_send_message(connection, msg);
3251+
3252+ return 0;
3253+}
3254+
3255+static struct pending_req *find_request(const DBusPendingCall *call)
3256+{
3257+ GSList *l;
3258+
3259+ for (l = pending; l; l = l->next) {
3260+ struct pending_req *req = l->data;
3261+
3262+ if (req->call == call)
3263+ return req;
3264+ }
3265+
3266+ return NULL;
3267+}
3268+
3269+static void pending_req_finalize(void *data)
3270+{
3271+ struct pending_req *req = data;
3272+
3273+ if (!dbus_pending_call_get_completed(req->call))
3274+ dbus_pending_call_cancel(req->call);
3275+
3276+ dbus_pending_call_unref(req->call);
3277+ g_free(req);
3278+}
3279+
3280+static void remove_pending(DBusPendingCall *call)
3281+{
3282+ struct pending_req *req = find_request(call);
3283+
3284+ pending = g_slist_remove(pending, req);
3285+ pending_req_finalize(req);
3286+}
3287+
3288+static void stop_ringtone_reply(DBusPendingCall *call, void *user_data)
3289+{
3290+ struct csd_call *coming = user_data;
3291+
3292+ remove_pending(call);
3293+ answer_call(coming);
3294+}
3295+
3296+static int stop_ringtone_and_answer(struct csd_call *call)
3297+{
3298+ int ret;
3299+
3300+ ret = send_method_call(OHM_BUS_NAME, OHM_PATH,
3301+ OHM_INTERFACE, "StopRingtone",
3302+ stop_ringtone_reply, call,
3303+ DBUS_TYPE_INVALID);
3304+ if (ret < 0)
3305+ return answer_call(call);
3306+
3307+ return 0;
3308+}
3309+
3310+static int split_call(struct csd_call *call)
3311+{
3312+ DBusMessage *msg;
3313+
3314+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
3315+ call->object_path,
3316+ CSD_CALL_INSTANCE,
3317+ "Split");
3318+ if (!msg) {
3319+ error("Unable to allocate new D-Bus message");
3320+ return -ENOMEM;
3321+ }
3322+
3323+ g_dbus_send_message(connection, msg);
3324+
3325+ return 0;
3326+}
3327+
3328+static int unhold_call(struct csd_call *call)
3329+{
3330+ DBusMessage *msg;
3331+
3332+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3333+ CSD_CALL_INTERFACE,
3334+ "Unhold");
3335+ if (!msg) {
3336+ error("Unable to allocate new D-Bus message");
3337+ return -ENOMEM;
3338+ }
3339+
3340+ g_dbus_send_message(connection, msg);
3341+
3342+ return 0;
3343+}
3344+
3345+static int hold_call(struct csd_call *call)
3346+{
3347+ DBusMessage *msg;
3348+
3349+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3350+ CSD_CALL_INTERFACE,
3351+ "Hold");
3352+ if (!msg) {
3353+ error("Unable to allocate new D-Bus message");
3354+ return -ENOMEM;
3355+ }
3356+
3357+ g_dbus_send_message(connection, msg);
3358+
3359+ return 0;
3360+}
3361+
3362+static int swap_calls(void)
3363+{
3364+ DBusMessage *msg;
3365+
3366+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3367+ CSD_CALL_INTERFACE,
3368+ "Swap");
3369+ if (!msg) {
3370+ error("Unable to allocate new D-Bus message");
3371+ return -ENOMEM;
3372+ }
3373+
3374+ g_dbus_send_message(connection, msg);
3375+
3376+ return 0;
3377+}
3378+
3379+static int create_conference(void)
3380+{
3381+ DBusMessage *msg;
3382+
3383+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3384+ CSD_CALL_INTERFACE,
3385+ "Conference");
3386+ if (!msg) {
3387+ error("Unable to allocate new D-Bus message");
3388+ return -ENOMEM;
3389+ }
3390+
3391+ g_dbus_send_message(connection, msg);
3392+
3393+ return 0;
3394+}
3395+
3396+static int call_transfer(void)
3397+{
3398+ DBusMessage *msg;
3399+
3400+ msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3401+ CSD_CALL_INTERFACE,
3402+ "Transfer");
3403+ if (!msg) {
3404+ error("Unable to allocate new D-Bus message");
3405+ return -ENOMEM;
3406+ }
3407+
3408+ g_dbus_send_message(connection, msg);
3409+
3410+ return 0;
3411+}
3412+
3413+static int number_type(const char *number)
3414+{
3415+ if (number == NULL)
3416+ return NUMBER_TYPE_TELEPHONY;
3417+
3418+ if (number[0] == '+' || strncmp(number, "00", 2) == 0)
3419+ return NUMBER_TYPE_INTERNATIONAL;
3420+
3421+ return NUMBER_TYPE_TELEPHONY;
3422+}
3423+
3424+void telephony_device_connected(void *telephony_device)
3425+{
3426+ struct csd_call *coming;
3427+
3428+ DBG("telephony-maemo6: device %p connected", telephony_device);
3429+
3430+ coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
3431+ if (coming) {
3432+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
3433+ telephony_call_waiting_ind(coming->number,
3434+ number_type(coming->number));
3435+ else
3436+ telephony_incoming_call_ind(coming->number,
3437+ number_type(coming->number));
3438+ }
3439+}
3440+
3441+static void remove_pending_by_data(gpointer data, gpointer user_data)
3442+{
3443+ struct pending_req *req = data;
3444+
3445+ if (req->user_data == user_data) {
3446+ pending = g_slist_remove(pending, req);
3447+ pending_req_finalize(req);
3448+ }
3449+}
3450+
3451+void telephony_device_disconnected(void *telephony_device)
3452+{
3453+ DBG("telephony-maemo6: device %p disconnected", telephony_device);
3454+ events_enabled = FALSE;
3455+
3456+ g_slist_foreach(pending, remove_pending_by_data, telephony_device);
3457+}
3458+
3459+void telephony_event_reporting_req(void *telephony_device, int ind)
3460+{
3461+ events_enabled = ind == 1 ? TRUE : FALSE;
3462+
3463+ telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
3464+}
3465+
3466+void telephony_response_and_hold_req(void *telephony_device, int rh)
3467+{
3468+ telephony_response_and_hold_rsp(telephony_device,
3469+ CME_ERROR_NOT_SUPPORTED);
3470+}
3471+
3472+void telephony_terminate_call_req(void *telephony_device)
3473+{
3474+ struct csd_call *call;
3475+ struct csd_call *alerting;
3476+ int err;
3477+
3478+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
3479+ if (!call)
3480+ call = find_non_idle_call();
3481+
3482+ if (!call) {
3483+ error("No active call");
3484+ telephony_terminate_call_rsp(telephony_device,
3485+ CME_ERROR_NOT_ALLOWED);
3486+ return;
3487+ }
3488+
3489+ alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING);
3490+ if (call->on_hold && alerting)
3491+ err = release_call(alerting);
3492+ else if (call->conference)
3493+ err = release_conference();
3494+ else
3495+ err = release_call(call);
3496+
3497+ if (err < 0)
3498+ telephony_terminate_call_rsp(telephony_device,
3499+ CME_ERROR_AG_FAILURE);
3500+ else
3501+ telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
3502+}
3503+
3504+void telephony_answer_call_req(void *telephony_device)
3505+{
3506+ struct csd_call *call;
3507+
3508+ call = find_call_with_status(CSD_CALL_STATUS_COMING);
3509+ if (!call)
3510+ call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
3511+
3512+ if (!call)
3513+ call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
3514+
3515+ if (!call)
3516+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
3517+
3518+ if (!call) {
3519+ telephony_answer_call_rsp(telephony_device,
3520+ CME_ERROR_NOT_ALLOWED);
3521+ return;
3522+ }
3523+
3524+ if (stop_ringtone_and_answer(call) < 0)
3525+ telephony_answer_call_rsp(telephony_device,
3526+ CME_ERROR_AG_FAILURE);
3527+ else
3528+ telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
3529+}
3530+
3531+static void create_call_reply(DBusPendingCall *call, void *user_data)
3532+{
3533+ DBusError err;
3534+ DBusMessage *reply;
3535+ void *telephony_device = user_data;
3536+
3537+ reply = dbus_pending_call_steal_reply(call);
3538+
3539+ dbus_error_init(&err);
3540+ if (dbus_set_error_from_message(&err, reply)) {
3541+ error("csd replied with an error: %s, %s",
3542+ err.name, err.message);
3543+ if (g_strcmp0(err.name,
3544+ "com.nokia.csd.Call.Error.CSInactive") == 0)
3545+ telephony_dial_number_rsp(telephony_device,
3546+ CME_ERROR_NO_NETWORK_SERVICE);
3547+ else
3548+ telephony_dial_number_rsp(telephony_device,
3549+ CME_ERROR_AG_FAILURE);
3550+ dbus_error_free(&err);
3551+ } else
3552+ telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
3553+
3554+ dbus_message_unref(reply);
3555+ remove_pending(call);
3556+}
3557+
3558+void telephony_last_dialed_number_req(void *telephony_device)
3559+{
3560+ int ret;
3561+
3562+ DBG("telephony-maemo6: last dialed number request");
3563+
3564+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3565+ CSD_CALL_INTERFACE, "CreateFromLast",
3566+ create_call_reply, telephony_device,
3567+ DBUS_TYPE_INVALID);
3568+ if (ret < 0)
3569+ telephony_dial_number_rsp(telephony_device,
3570+ CME_ERROR_AG_FAILURE);
3571+}
3572+
3573+static const char *memory_dial_lookup(int location)
3574+{
3575+ if (location == 1)
3576+ return vmbx;
3577+ else
3578+ return NULL;
3579+}
3580+
3581+void telephony_dial_number_req(void *telephony_device, const char *number)
3582+{
3583+ int ret;
3584+
3585+ DBG("telephony-maemo6: dial request to %s", number);
3586+
3587+ if (strncmp(number, "*31#", 4) == 0)
3588+ number += 4;
3589+ else if (strncmp(number, "#31#", 4) == 0)
3590+ number += 4;
3591+ else if (number[0] == '>') {
3592+ const char *location = &number[1];
3593+
3594+ number = memory_dial_lookup(strtol(&number[1], NULL, 0));
3595+ if (!number) {
3596+ error("No number at memory location %s", location);
3597+ telephony_dial_number_rsp(telephony_device,
3598+ CME_ERROR_INVALID_INDEX);
3599+ return;
3600+ }
3601+ }
3602+
3603+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3604+ CSD_CALL_INTERFACE, "Create",
3605+ create_call_reply, telephony_device,
3606+ DBUS_TYPE_STRING, &number,
3607+ DBUS_TYPE_INVALID);
3608+ if (ret < 0)
3609+ telephony_dial_number_rsp(telephony_device,
3610+ CME_ERROR_AG_FAILURE);
3611+}
3612+
3613+static void start_dtmf_reply(DBusPendingCall *call, void *user_data)
3614+{
3615+ DBusError err;
3616+ DBusMessage *reply;
3617+
3618+ reply = dbus_pending_call_steal_reply(call);
3619+
3620+ dbus_error_init(&err);
3621+ if (dbus_set_error_from_message(&err, reply)) {
3622+ error("csd replied with an error: %s, %s",
3623+ err.name, err.message);
3624+
3625+ dbus_error_free(&err);
3626+ } else
3627+ send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3628+ CSD_CALL_INTERFACE, "StopDTMF",
3629+ NULL, NULL,
3630+ DBUS_TYPE_INVALID);
3631+
3632+ dbus_message_unref(reply);
3633+ remove_pending(call);
3634+}
3635+
3636+static void start_dtmf(void *telephony_device, char tone)
3637+{
3638+ int ret;
3639+
3640+ /*
3641+ * Stop tone immediately, modem will place it in queue and play
3642+ * required time.
3643+ */
3644+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
3645+ CSD_CALL_INTERFACE, "StartDTMF",
3646+ start_dtmf_reply, NULL,
3647+ DBUS_TYPE_BYTE, &tone,
3648+ DBUS_TYPE_INVALID);
3649+ if (ret < 0) {
3650+ telephony_transmit_dtmf_rsp(telephony_device,
3651+ CME_ERROR_AG_FAILURE);
3652+ return;
3653+ }
3654+
3655+ telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
3656+}
3657+
3658+static int tonegen_startevent(char tone)
3659+{
3660+ int ret;
3661+ dbus_uint32_t event_tone;
3662+ dbus_int32_t dbm0 = -15;
3663+ dbus_uint32_t duration = 150;
3664+
3665+ switch (tone) {
3666+ case '*':
3667+ event_tone = DTMF_ASTERISK;
3668+ break;
3669+ case '#':
3670+ event_tone = DTMF_HASHMARK;
3671+ break;
3672+ case 'A':
3673+ event_tone = DTMF_A;
3674+ break;
3675+ case 'B':
3676+ event_tone = DTMF_B;
3677+ break;
3678+ case 'C':
3679+ event_tone = DTMF_C;
3680+ break;
3681+ case 'D':
3682+ event_tone = DTMF_D;
3683+ break;
3684+ default:
3685+ ret = g_ascii_digit_value(tone);
3686+ if (ret < 0)
3687+ return -EINVAL;
3688+ event_tone = ret;
3689+ }
3690+
3691+ ret = send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
3692+ TONEGEN_INTERFACE, "StartEventTone",
3693+ NULL, NULL,
3694+ DBUS_TYPE_UINT32, &event_tone,
3695+ DBUS_TYPE_INT32, &dbm0,
3696+ DBUS_TYPE_UINT32, &duration,
3697+ DBUS_TYPE_INVALID);
3698+ return ret;
3699+}
3700+
3701+static gboolean stop_feedback_tone(gpointer user_data)
3702+{
3703+ if (g_slist_length(tones) > 0) {
3704+ gpointer ptone;
3705+ int ret;
3706+
3707+ send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
3708+ TONEGEN_INTERFACE, "StopTone",
3709+ NULL, NULL,
3710+ DBUS_TYPE_INVALID);
3711+
3712+ ptone = g_slist_nth_data(tones, 0);
3713+ tones = g_slist_remove(tones, ptone);
3714+
3715+ ret = tonegen_startevent(GPOINTER_TO_UINT(ptone));
3716+ if (ret < 0)
3717+ goto done;
3718+
3719+ return TRUE;
3720+ }
3721+done:
3722+ return FALSE;
3723+}
3724+
3725+static void tones_timer_notify(gpointer data)
3726+{
3727+ send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
3728+ TONEGEN_INTERFACE, "StopTone",
3729+ NULL, NULL,
3730+ DBUS_TYPE_INVALID);
3731+ g_slist_free(tones);
3732+ tones = NULL;
3733+
3734+ create_tones_timer = 0;
3735+}
3736+
3737+static void start_feedback_tone(char tone)
3738+{
3739+ if (!create_tones_timer) {
3740+ int ret;
3741+
3742+ ret = tonegen_startevent(tone);
3743+ if (ret < 0)
3744+ return;
3745+
3746+ create_tones_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
3747+ FEEDBACK_TONE_DURATION,
3748+ stop_feedback_tone,
3749+ NULL,
3750+ tones_timer_notify);
3751+ } else {
3752+ glong dtmf_tone = tone;
3753+
3754+ DBG("add %c to queue", tone);
3755+ tones = g_slist_append(tones, GUINT_TO_POINTER(dtmf_tone));
3756+ }
3757+}
3758+
3759+void telephony_transmit_dtmf_req(void *telephony_device, char tone)
3760+{
3761+ DBG("telephony-maemo6: transmit dtmf: %c", tone);
3762+
3763+ start_dtmf(telephony_device, tone);
3764+
3765+ if (!find_call_with_status(CSD_CALL_STATUS_ACTIVE))
3766+ error("No active call");
3767+ else
3768+ start_feedback_tone(tone);
3769+}
3770+
3771+void telephony_subscriber_number_req(void *telephony_device)
3772+{
3773+ DBG("telephony-maemo6: subscriber number request");
3774+ if (msisdn)
3775+ telephony_subscriber_number_ind(msisdn,
3776+ number_type(msisdn),
3777+ SUBSCRIBER_SERVICE_VOICE);
3778+ telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
3779+}
3780+
3781+static int csd_status_to_hfp(struct csd_call *call)
3782+{
3783+ switch (call->status) {
3784+ case CSD_CALL_STATUS_IDLE:
3785+ case CSD_CALL_STATUS_MO_RELEASE:
3786+ case CSD_CALL_STATUS_MT_RELEASE:
3787+ case CSD_CALL_STATUS_TERMINATED:
3788+ return -1;
3789+ case CSD_CALL_STATUS_CREATE:
3790+ return CALL_STATUS_DIALING;
3791+ case CSD_CALL_STATUS_WAITING:
3792+ return CALL_STATUS_WAITING;
3793+ case CSD_CALL_STATUS_PROCEEDING:
3794+ /* PROCEEDING can happen in outgoing/incoming */
3795+ if (call->originating)
3796+ return CALL_STATUS_DIALING;
3797+
3798+ /*
3799+ * PROCEEDING is followed by WAITING CSD status, therefore
3800+ * second incoming call status indication is set immediately
3801+ * to waiting.
3802+ */
3803+ if (g_slist_length(active_calls) > 0)
3804+ return CALL_STATUS_WAITING;
3805+
3806+ return CALL_STATUS_INCOMING;
3807+ case CSD_CALL_STATUS_COMING:
3808+ if (g_slist_length(active_calls) > 0)
3809+ return CALL_STATUS_WAITING;
3810+
3811+ return CALL_STATUS_INCOMING;
3812+ case CSD_CALL_STATUS_MO_ALERTING:
3813+ return CALL_STATUS_ALERTING;
3814+ case CSD_CALL_STATUS_MT_ALERTING:
3815+ return CALL_STATUS_INCOMING;
3816+ case CSD_CALL_STATUS_ANSWERED:
3817+ case CSD_CALL_STATUS_ACTIVE:
3818+ case CSD_CALL_STATUS_RECONNECT_PENDING:
3819+ case CSD_CALL_STATUS_SWAP_INITIATED:
3820+ case CSD_CALL_STATUS_HOLD_INITIATED:
3821+ return CALL_STATUS_ACTIVE;
3822+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
3823+ case CSD_CALL_STATUS_HOLD:
3824+ return CALL_STATUS_HELD;
3825+ default:
3826+ return -1;
3827+ }
3828+}
3829+
3830+void telephony_list_current_calls_req(void *telephony_device)
3831+{
3832+ GSList *l;
3833+ int i;
3834+
3835+ DBG("telephony-maemo6: list current calls request");
3836+
3837+ for (l = calls, i = 1; l != NULL; l = l->next, i++) {
3838+ struct csd_call *call = l->data;
3839+ int status, direction, multiparty;
3840+
3841+ status = csd_status_to_hfp(call);
3842+ if (status < 0)
3843+ continue;
3844+
3845+ direction = call->originating ?
3846+ CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
3847+
3848+ multiparty = call->conference ?
3849+ CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
3850+
3851+ telephony_list_current_call_ind(i, direction, status,
3852+ CALL_MODE_VOICE, multiparty,
3853+ call->number,
3854+ number_type(call->number));
3855+ }
3856+
3857+ telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
3858+}
3859+
3860+void telephony_operator_selection_req(void *telephony_device)
3861+{
3862+ telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
3863+ net.operator_name ? net.operator_name : "");
3864+ telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
3865+}
3866+
3867+static void foreach_call_with_status(int status,
3868+ int (*func)(struct csd_call *call))
3869+{
3870+ GSList *l;
3871+
3872+ for (l = calls; l != NULL; l = l->next) {
3873+ struct csd_call *call = l->data;
3874+
3875+ if (call->status == status)
3876+ func(call);
3877+ }
3878+}
3879+
3880+void telephony_call_hold_req(void *telephony_device, const char *cmd)
3881+{
3882+ const char *idx;
3883+ struct csd_call *call;
3884+ int err = 0;
3885+
3886+ DBG("telephony-maemo6: got call hold request %s", cmd);
3887+
3888+ if (strlen(cmd) > 1)
3889+ idx = &cmd[1];
3890+ else
3891+ idx = NULL;
3892+
3893+ if (idx)
3894+ call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
3895+ else
3896+ call = NULL;
3897+
3898+ switch (cmd[0]) {
3899+ case '0':
3900+ if (find_call_with_status(CSD_CALL_STATUS_WAITING))
3901+ foreach_call_with_status(CSD_CALL_STATUS_WAITING,
3902+ release_call);
3903+ else
3904+ foreach_call_with_status(CSD_CALL_STATUS_HOLD,
3905+ release_call);
3906+ break;
3907+ case '1':
3908+ if (idx) {
3909+ if (call)
3910+ err = release_call(call);
3911+ break;
3912+ }
3913+ foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
3914+ call = find_call_with_status(CSD_CALL_STATUS_WAITING);
3915+ if (call)
3916+ err = answer_call(call);
3917+ break;
3918+ case '2':
3919+ if (idx) {
3920+ if (call)
3921+ err = split_call(call);
3922+ } else {
3923+ struct csd_call *held, *wait;
3924+
3925+ call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
3926+ held = find_call_with_status(CSD_CALL_STATUS_HOLD);
3927+ wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
3928+
3929+ if (wait)
3930+ err = answer_call(wait);
3931+ else if (call && held)
3932+ err = swap_calls();
3933+ else {
3934+ if (call)
3935+ err = hold_call(call);
3936+ if (held)
3937+ err = unhold_call(held);
3938+ }
3939+ }
3940+ break;
3941+ case '3':
3942+ if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
3943+ find_call_with_status(CSD_CALL_STATUS_WAITING))
3944+ err = create_conference();
3945+ break;
3946+ case '4':
3947+ err = call_transfer();
3948+ break;
3949+ default:
3950+ DBG("Unknown call hold request");
3951+ break;
3952+ }
3953+
3954+ if (err)
3955+ telephony_call_hold_rsp(telephony_device,
3956+ CME_ERROR_AG_FAILURE);
3957+ else
3958+ telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
3959+}
3960+
3961+void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
3962+{
3963+ DBG("telephony-maemo6: got %s NR and EC request",
3964+ enable ? "enable" : "disable");
3965+ telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
3966+}
3967+
3968+void telephony_key_press_req(void *telephony_device, const char *keys)
3969+{
3970+ struct csd_call *active, *waiting;
3971+ int err;
3972+
3973+ DBG("telephony-maemo6: got key press request for %s", keys);
3974+
3975+ waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
3976+ if (!waiting)
3977+ waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
3978+ if (!waiting)
3979+ waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
3980+
3981+ active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
3982+
3983+ if (waiting)
3984+ err = answer_call(waiting);
3985+ else if (active)
3986+ err = release_call(active);
3987+ else
3988+ err = 0;
3989+
3990+ if (err < 0)
3991+ telephony_key_press_rsp(telephony_device,
3992+ CME_ERROR_AG_FAILURE);
3993+ else
3994+ telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
3995+}
3996+
3997+void telephony_voice_dial_req(void *telephony_device, gboolean enable)
3998+{
3999+ DBG("telephony-maemo6: got %s voice dial request",
4000+ enable ? "enable" : "disable");
4001+
4002+ telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
4003+}
4004+
4005+static void handle_incoming_call(DBusMessage *msg)
4006+{
4007+ const char *number, *call_path;
4008+ struct csd_call *call;
4009+
4010+ if (!dbus_message_get_args(msg, NULL,
4011+ DBUS_TYPE_OBJECT_PATH, &call_path,
4012+ DBUS_TYPE_STRING, &number,
4013+ DBUS_TYPE_INVALID)) {
4014+ error("Unexpected parameters in Call.Coming() signal");
4015+ return;
4016+ }
4017+
4018+ call = find_call(call_path);
4019+ if (!call) {
4020+ error("Didn't find any matching call object for %s",
4021+ call_path);
4022+ return;
4023+ }
4024+
4025+ DBG("Incoming call to %s from number %s", call_path, number);
4026+
4027+ g_free(call->number);
4028+ call->number = g_strdup(number);
4029+
4030+ if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
4031+ find_call_with_status(CSD_CALL_STATUS_HOLD))
4032+ telephony_call_waiting_ind(call->number,
4033+ number_type(call->number));
4034+ else
4035+ telephony_incoming_call_ind(call->number,
4036+ number_type(call->number));
4037+
4038+ telephony_update_indicator(maemo_indicators, "callsetup",
4039+ EV_CALLSETUP_INCOMING);
4040+}
4041+
4042+static void handle_outgoing_call(DBusMessage *msg)
4043+{
4044+ const char *number, *call_path;
4045+ struct csd_call *call;
4046+
4047+ if (!dbus_message_get_args(msg, NULL,
4048+ DBUS_TYPE_OBJECT_PATH, &call_path,
4049+ DBUS_TYPE_STRING, &number,
4050+ DBUS_TYPE_INVALID)) {
4051+ error("Unexpected parameters in Call.Created() signal");
4052+ return;
4053+ }
4054+
4055+ call = find_call(call_path);
4056+ if (!call) {
4057+ error("Didn't find any matching call object for %s",
4058+ call_path);
4059+ return;
4060+ }
4061+
4062+ DBG("Outgoing call from %s to number %s", call_path, number);
4063+
4064+ g_free(call->number);
4065+ call->number = g_strdup(number);
4066+
4067+ if (create_request_timer) {
4068+ g_source_remove(create_request_timer);
4069+ create_request_timer = 0;
4070+ }
4071+}
4072+
4073+static gboolean create_timeout(gpointer user_data)
4074+{
4075+ telephony_update_indicator(maemo_indicators, "callsetup",
4076+ EV_CALLSETUP_INACTIVE);
4077+ create_request_timer = 0;
4078+ return FALSE;
4079+}
4080+
4081+static void handle_create_requested(DBusMessage *msg)
4082+{
4083+ DBG("Call.CreateRequested()");
4084+
4085+ if (create_request_timer)
4086+ g_source_remove(create_request_timer);
4087+
4088+ create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
4089+
4090+ telephony_update_indicator(maemo_indicators, "callsetup",
4091+ EV_CALLSETUP_OUTGOING);
4092+}
4093+
4094+static void call_set_status(struct csd_call *call, dbus_uint32_t status)
4095+{
4096+ dbus_uint32_t prev_status;
4097+ int callheld = telephony_get_indicator(maemo_indicators, "callheld");
4098+
4099+ prev_status = call->status;
4100+ DBG("Call %s changed from %s to %s", call->object_path,
4101+ call_status_str[prev_status], call_status_str[status]);
4102+
4103+ if (prev_status == status) {
4104+ DBG("Ignoring CSD Call state change to existing state");
4105+ return;
4106+ }
4107+
4108+ call->status = (int) status;
4109+
4110+ switch (status) {
4111+ case CSD_CALL_STATUS_IDLE:
4112+ if (call->setup) {
4113+ telephony_update_indicator(maemo_indicators,
4114+ "callsetup",
4115+ EV_CALLSETUP_INACTIVE);
4116+ if (!call->originating)
4117+ telephony_calling_stopped_ind();
4118+ }
4119+
4120+ g_free(call->number);
4121+ call->number = NULL;
4122+ call->originating = FALSE;
4123+ call->emergency = FALSE;
4124+ call->on_hold = FALSE;
4125+ call->conference = FALSE;
4126+ call->setup = FALSE;
4127+ break;
4128+ case CSD_CALL_STATUS_CREATE:
4129+ call->originating = TRUE;
4130+ call->setup = TRUE;
4131+ break;
4132+ case CSD_CALL_STATUS_COMING:
4133+ call->originating = FALSE;
4134+ call->setup = TRUE;
4135+ break;
4136+ case CSD_CALL_STATUS_PROCEEDING:
4137+ break;
4138+ case CSD_CALL_STATUS_MO_ALERTING:
4139+ telephony_update_indicator(maemo_indicators, "callsetup",
4140+ EV_CALLSETUP_ALERTING);
4141+ break;
4142+ case CSD_CALL_STATUS_MT_ALERTING:
4143+ /* Some headsets expect incoming call notification before they
4144+ * can send ATA command. When call changed status from waiting
4145+ * to alerting we need to send missing notification. Otherwise
4146+ * headsets like Nokia BH-108 or BackBeat 903 are unable to
4147+ * answer incoming call that was previously waiting. */
4148+ if (prev_status == CSD_CALL_STATUS_WAITING)
4149+ telephony_incoming_call_ind(call->number,
4150+ number_type(call->number));
4151+ break;
4152+ case CSD_CALL_STATUS_WAITING:
4153+ break;
4154+ case CSD_CALL_STATUS_ANSWERED:
4155+ break;
4156+ case CSD_CALL_STATUS_ACTIVE:
4157+ if (call->on_hold) {
4158+ call->on_hold = FALSE;
4159+ if (find_call_with_status(CSD_CALL_STATUS_HOLD))
4160+ telephony_update_indicator(maemo_indicators,
4161+ "callheld",
4162+ EV_CALLHELD_MULTIPLE);
4163+ else
4164+ telephony_update_indicator(maemo_indicators,
4165+ "callheld",
4166+ EV_CALLHELD_NONE);
4167+ } else {
4168+ if (!g_slist_find(active_calls, call))
4169+ active_calls = g_slist_prepend(active_calls, call);
4170+ if (g_slist_length(active_calls) == 1)
4171+ telephony_update_indicator(maemo_indicators,
4172+ "call",
4173+ EV_CALL_ACTIVE);
4174+ /* Upgrade callheld status if necessary */
4175+ if (callheld == EV_CALLHELD_ON_HOLD)
4176+ telephony_update_indicator(maemo_indicators,
4177+ "callheld",
4178+ EV_CALLHELD_MULTIPLE);
4179+ telephony_update_indicator(maemo_indicators,
4180+ "callsetup",
4181+ EV_CALLSETUP_INACTIVE);
4182+ if (!call->originating)
4183+ telephony_calling_stopped_ind();
4184+ call->setup = FALSE;
4185+ }
4186+ break;
4187+ case CSD_CALL_STATUS_MO_RELEASE:
4188+ case CSD_CALL_STATUS_MT_RELEASE:
4189+ active_calls = g_slist_remove(active_calls, call);
4190+ if (g_slist_length(active_calls) == 0)
4191+ telephony_update_indicator(maemo_indicators, "call",
4192+ EV_CALL_INACTIVE);
4193+
4194+ if (create_tones_timer)
4195+ g_source_remove(create_tones_timer);
4196+ break;
4197+ case CSD_CALL_STATUS_HOLD_INITIATED:
4198+ break;
4199+ case CSD_CALL_STATUS_HOLD:
4200+ call->on_hold = TRUE;
4201+ if (find_non_held_call())
4202+ telephony_update_indicator(maemo_indicators,
4203+ "callheld",
4204+ EV_CALLHELD_MULTIPLE);
4205+ else
4206+ telephony_update_indicator(maemo_indicators,
4207+ "callheld",
4208+ EV_CALLHELD_ON_HOLD);
4209+ break;
4210+ case CSD_CALL_STATUS_RETRIEVE_INITIATED:
4211+ break;
4212+ case CSD_CALL_STATUS_RECONNECT_PENDING:
4213+ break;
4214+ case CSD_CALL_STATUS_TERMINATED:
4215+ if (call->on_hold &&
4216+ !find_call_with_status(CSD_CALL_STATUS_HOLD)) {
4217+ telephony_update_indicator(maemo_indicators,
4218+ "callheld",
4219+ EV_CALLHELD_NONE);
4220+ return;
4221+ }
4222+
4223+ if (callheld == EV_CALLHELD_MULTIPLE &&
4224+ find_call_with_status(CSD_CALL_STATUS_HOLD) &&
4225+ !find_call_with_status(CSD_CALL_STATUS_ACTIVE))
4226+ telephony_update_indicator(maemo_indicators,
4227+ "callheld",
4228+ EV_CALLHELD_ON_HOLD);
4229+ break;
4230+ case CSD_CALL_STATUS_SWAP_INITIATED:
4231+ break;
4232+ default:
4233+ error("Unknown call status %u", status);
4234+ break;
4235+ }
4236+}
4237+
4238+static void handle_call_status(DBusMessage *msg, const char *call_path)
4239+{
4240+ struct csd_call *call;
4241+ dbus_uint32_t status, cause_type, cause;
4242+
4243+ if (!dbus_message_get_args(msg, NULL,
4244+ DBUS_TYPE_UINT32, &status,
4245+ DBUS_TYPE_UINT32, &cause_type,
4246+ DBUS_TYPE_UINT32, &cause,
4247+ DBUS_TYPE_INVALID)) {
4248+ error("Unexpected paramters in Instance.CallStatus() signal");
4249+ return;
4250+ }
4251+
4252+ call = find_call(call_path);
4253+ if (!call) {
4254+ error("Didn't find any matching call object for %s",
4255+ call_path);
4256+ return;
4257+ }
4258+
4259+ if (status > 16) {
4260+ error("Invalid call status %u", status);
4261+ return;
4262+ }
4263+
4264+ call_set_status(call, status);
4265+}
4266+
4267+static void handle_conference(DBusMessage *msg, gboolean joined)
4268+{
4269+ const char *path;
4270+ struct csd_call *call;
4271+
4272+ if (!dbus_message_get_args(msg, NULL,
4273+ DBUS_TYPE_OBJECT_PATH, &path,
4274+ DBUS_TYPE_INVALID)) {
4275+ error("Unexpected parameters in Conference.%s",
4276+ dbus_message_get_member(msg));
4277+ return;
4278+ }
4279+
4280+ call = find_call(path);
4281+ if (!call) {
4282+ error("Conference signal for unknown call %s", path);
4283+ return;
4284+ }
4285+
4286+ DBG("Call %s %s the conference", path, joined ? "joined" : "left");
4287+
4288+ call->conference = joined;
4289+}
4290+
4291+static uint8_t str2status(const char *state)
4292+{
4293+ if (g_strcmp0(state, "Home") == 0)
4294+ return NETWORK_REG_STATUS_HOME;
4295+ else if (g_strcmp0(state, "Roaming") == 0)
4296+ return NETWORK_REG_STATUS_ROAMING;
4297+ else if (g_strcmp0(state, "Offline") == 0)
4298+ return NETWORK_REG_STATUS_OFFLINE;
4299+ else if (g_strcmp0(state, "Searching") == 0)
4300+ return NETWORK_REG_STATUS_SEARCHING;
4301+ else if (g_strcmp0(state, "NoSim") == 0)
4302+ return NETWORK_REG_STATUS_NO_SIM;
4303+ else if (g_strcmp0(state, "Poweroff") == 0)
4304+ return NETWORK_REG_STATUS_POWEROFF;
4305+ else if (g_strcmp0(state, "Powersafe") == 0)
4306+ return NETWORK_REG_STATUS_POWERSAFE;
4307+ else if (g_strcmp0(state, "NoCoverage") == 0)
4308+ return NETWORK_REG_STATUS_NO_COVERAGE;
4309+ else if (g_strcmp0(state, "Reject") == 0)
4310+ return NETWORK_REG_STATUS_REJECTED;
4311+ else
4312+ return NETWORK_REG_STATUS_UNKOWN;
4313+}
4314+
4315+static void update_registration_status(const char *status)
4316+{
4317+ uint8_t new_status;
4318+
4319+ new_status = str2status(status);
4320+
4321+ if (net.status == new_status)
4322+ return;
4323+
4324+ switch (new_status) {
4325+ case NETWORK_REG_STATUS_HOME:
4326+ telephony_update_indicator(maemo_indicators, "roam",
4327+ EV_ROAM_INACTIVE);
4328+ if (net.status > NETWORK_REG_STATUS_ROAMING)
4329+ telephony_update_indicator(maemo_indicators,
4330+ "service",
4331+ EV_SERVICE_PRESENT);
4332+ break;
4333+ case NETWORK_REG_STATUS_ROAMING:
4334+ telephony_update_indicator(maemo_indicators, "roam",
4335+ EV_ROAM_ACTIVE);
4336+ if (net.status > NETWORK_REG_STATUS_ROAMING)
4337+ telephony_update_indicator(maemo_indicators,
4338+ "service",
4339+ EV_SERVICE_PRESENT);
4340+ break;
4341+ case NETWORK_REG_STATUS_OFFLINE:
4342+ case NETWORK_REG_STATUS_SEARCHING:
4343+ case NETWORK_REG_STATUS_NO_SIM:
4344+ case NETWORK_REG_STATUS_POWEROFF:
4345+ case NETWORK_REG_STATUS_POWERSAFE:
4346+ case NETWORK_REG_STATUS_NO_COVERAGE:
4347+ case NETWORK_REG_STATUS_REJECTED:
4348+ case NETWORK_REG_STATUS_UNKOWN:
4349+ if (net.status < NETWORK_REG_STATUS_OFFLINE)
4350+ telephony_update_indicator(maemo_indicators,
4351+ "service",
4352+ EV_SERVICE_NONE);
4353+ break;
4354+ }
4355+
4356+ net.status = new_status;
4357+
4358+ DBG("telephony-maemo6: registration status changed: %s", status);
4359+}
4360+
4361+static void handle_registration_changed(DBusMessage *msg)
4362+{
4363+ const char *status;
4364+
4365+ if (!dbus_message_get_args(msg, NULL,
4366+ DBUS_TYPE_STRING, &status,
4367+ DBUS_TYPE_INVALID)) {
4368+ error("Unexpected parameters in RegistrationChanged");
4369+ return;
4370+ }
4371+
4372+ update_registration_status(status);
4373+}
4374+
4375+static void update_signal_strength(int32_t signal_bars)
4376+{
4377+ if (signal_bars < 0) {
4378+ DBG("signal strength smaller than expected: %d < 0",
4379+ signal_bars);
4380+ signal_bars = 0;
4381+ } else if (signal_bars > 5) {
4382+ DBG("signal strength greater than expected: %d > 5",
4383+ signal_bars);
4384+ signal_bars = 5;
4385+ }
4386+
4387+ if (net.signal_bars == signal_bars)
4388+ return;
4389+
4390+ telephony_update_indicator(maemo_indicators, "signal", signal_bars);
4391+
4392+ net.signal_bars = signal_bars;
4393+ DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
4394+}
4395+
4396+static void handle_signal_bars_changed(DBusMessage *msg)
4397+{
4398+ int32_t signal_bars;
4399+
4400+ if (!dbus_message_get_args(msg, NULL,
4401+ DBUS_TYPE_INT32, &signal_bars,
4402+ DBUS_TYPE_INVALID)) {
4403+ error("Unexpected parameters in SignalBarsChanged");
4404+ return;
4405+ }
4406+
4407+ update_signal_strength(signal_bars);
4408+}
4409+
4410+static gboolean iter_get_basic_args(DBusMessageIter *iter,
4411+ int first_arg_type, ...)
4412+{
4413+ int type;
4414+ va_list ap;
4415+
4416+ va_start(ap, first_arg_type);
4417+
4418+ for (type = first_arg_type; type != DBUS_TYPE_INVALID;
4419+ type = va_arg(ap, int)) {
4420+ void *value = va_arg(ap, void *);
4421+ int real_type = dbus_message_iter_get_arg_type(iter);
4422+
4423+ if (real_type != type) {
4424+ error("iter_get_basic_args: expected %c but got %c",
4425+ (char) type, (char) real_type);
4426+ break;
4427+ }
4428+
4429+ dbus_message_iter_get_basic(iter, value);
4430+ dbus_message_iter_next(iter);
4431+ }
4432+
4433+ va_end(ap);
4434+
4435+ return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
4436+}
4437+
4438+static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
4439+{
4440+ DBusError err;
4441+ DBusMessage *reply;
4442+ dbus_int32_t level;
4443+ int *value = user_data;
4444+
4445+ reply = dbus_pending_call_steal_reply(call);
4446+
4447+ dbus_error_init(&err);
4448+ if (dbus_set_error_from_message(&err, reply)) {
4449+ error("hald replied with an error: %s, %s",
4450+ err.name, err.message);
4451+ dbus_error_free(&err);
4452+ goto done;
4453+ }
4454+
4455+ if (!dbus_message_get_args(reply, NULL,
4456+ DBUS_TYPE_INT32, &level,
4457+ DBUS_TYPE_INVALID)) {
4458+ error("Unexpected args in hald reply");
4459+ goto done;
4460+ }
4461+
4462+ *value = (int) level;
4463+
4464+ if (value == &battchg_last)
4465+ DBG("telephony-maemo6: battery.charge_level.last_full is %d",
4466+ *value);
4467+ else if (value == &battchg_design)
4468+ DBG("telephony-maemo6: battery.charge_level.design is %d",
4469+ *value);
4470+ else
4471+ DBG("telephony-maemo6: battery.charge_level.current is %d",
4472+ *value);
4473+
4474+ if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
4475+ int new, max;
4476+
4477+ if (battchg_last > 0)
4478+ max = battchg_last;
4479+ else
4480+ max = battchg_design;
4481+
4482+ new = battchg_cur * 5 / max;
4483+
4484+ telephony_update_indicator(maemo_indicators, "battchg", new);
4485+ }
4486+
4487+done:
4488+ dbus_message_unref(reply);
4489+ remove_pending(call);
4490+}
4491+
4492+static void hal_get_integer(const char *path, const char *key, void *user_data)
4493+{
4494+ send_method_call("org.freedesktop.Hal", path,
4495+ "org.freedesktop.Hal.Device",
4496+ "GetPropertyInteger",
4497+ hal_battery_level_reply, user_data,
4498+ DBUS_TYPE_STRING, &key,
4499+ DBUS_TYPE_INVALID);
4500+}
4501+
4502+static void handle_hal_property_modified(DBusMessage *msg)
4503+{
4504+ DBusMessageIter iter, array;
4505+ dbus_int32_t num_changes;
4506+ const char *path;
4507+
4508+ path = dbus_message_get_path(msg);
4509+
4510+ dbus_message_iter_init(msg, &iter);
4511+
4512+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
4513+ error("Unexpected signature in hal PropertyModified signal");
4514+ return;
4515+ }
4516+
4517+ dbus_message_iter_get_basic(&iter, &num_changes);
4518+ dbus_message_iter_next(&iter);
4519+
4520+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
4521+ error("Unexpected signature in hal PropertyModified signal");
4522+ return;
4523+ }
4524+
4525+ dbus_message_iter_recurse(&iter, &array);
4526+
4527+ while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
4528+ DBusMessageIter prop;
4529+ const char *name;
4530+ dbus_bool_t added, removed;
4531+
4532+ dbus_message_iter_recurse(&array, &prop);
4533+
4534+ if (!iter_get_basic_args(&prop,
4535+ DBUS_TYPE_STRING, &name,
4536+ DBUS_TYPE_BOOLEAN, &added,
4537+ DBUS_TYPE_BOOLEAN, &removed,
4538+ DBUS_TYPE_INVALID)) {
4539+ error("Invalid hal PropertyModified parameters");
4540+ break;
4541+ }
4542+
4543+ if (g_str_equal(name, "battery.charge_level.last_full"))
4544+ hal_get_integer(path, name, &battchg_last);
4545+ else if (g_str_equal(name, "battery.charge_level.current"))
4546+ hal_get_integer(path, name, &battchg_cur);
4547+ else if (g_str_equal(name, "battery.charge_level.design"))
4548+ hal_get_integer(path, name, &battchg_design);
4549+
4550+ dbus_message_iter_next(&array);
4551+ }
4552+}
4553+
4554+static void csd_call_free(void *data)
4555+{
4556+ struct csd_call *call = data;
4557+
4558+ if (!call)
4559+ return;
4560+
4561+ g_free(call->object_path);
4562+ g_free(call->number);
4563+
4564+ g_slist_foreach(pending, remove_pending_by_data, call);
4565+
4566+ g_free(call);
4567+}
4568+
4569+static void parse_call_list(DBusMessageIter *iter)
4570+{
4571+ do {
4572+ DBusMessageIter call_iter;
4573+ struct csd_call *call;
4574+ const char *object_path, *number;
4575+ dbus_uint32_t status;
4576+ dbus_bool_t originating, terminating, emerg, on_hold, conf;
4577+
4578+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
4579+ error("Unexpected signature in GetCallInfoAll reply");
4580+ break;
4581+ }
4582+
4583+ dbus_message_iter_recurse(iter, &call_iter);
4584+
4585+ if (!iter_get_basic_args(&call_iter,
4586+ DBUS_TYPE_OBJECT_PATH, &object_path,
4587+ DBUS_TYPE_UINT32, &status,
4588+ DBUS_TYPE_BOOLEAN, &originating,
4589+ DBUS_TYPE_BOOLEAN, &terminating,
4590+ DBUS_TYPE_BOOLEAN, &emerg,
4591+ DBUS_TYPE_BOOLEAN, &on_hold,
4592+ DBUS_TYPE_BOOLEAN, &conf,
4593+ DBUS_TYPE_STRING, &number,
4594+ DBUS_TYPE_INVALID)) {
4595+ error("Parsing call D-Bus parameters failed");
4596+ break;
4597+ }
4598+
4599+ call = find_call(object_path);
4600+ if (!call) {
4601+ call = g_new0(struct csd_call, 1);
4602+ call->object_path = g_strdup(object_path);
4603+ calls = g_slist_append(calls, call);
4604+ DBG("telephony-maemo6: new csd call instance at %s",
4605+ object_path);
4606+ }
4607+
4608+ if (status == CSD_CALL_STATUS_IDLE)
4609+ continue;
4610+
4611+ /* CSD gives incorrect call_hold property sometimes */
4612+ if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
4613+ (call->status == CSD_CALL_STATUS_HOLD &&
4614+ !on_hold)) {
4615+ error("Conflicting call status and on_hold property!");
4616+ on_hold = call->status == CSD_CALL_STATUS_HOLD;
4617+ }
4618+
4619+ call->originating = originating;
4620+ call->on_hold = on_hold;
4621+ call->conference = conf;
4622+ g_free(call->number);
4623+ call->number = g_strdup(number);
4624+
4625+ /* Update indicators */
4626+ call_set_status(call, status);
4627+
4628+ } while (dbus_message_iter_next(iter));
4629+}
4630+
4631+static void update_operator_name(const char *name)
4632+{
4633+ if (name == NULL)
4634+ return;
4635+
4636+ g_free(net.operator_name);
4637+ net.operator_name = g_strndup(name, 16);
4638+ DBG("telephony-maemo6: operator name updated: %s", name);
4639+}
4640+
4641+static void get_property_reply(DBusPendingCall *call, void *user_data)
4642+{
4643+ char *prop = user_data;
4644+ DBusError err;
4645+ DBusMessage *reply;
4646+ DBusMessageIter iter, sub;
4647+
4648+ reply = dbus_pending_call_steal_reply(call);
4649+
4650+ dbus_error_init(&err);
4651+ if (dbus_set_error_from_message(&err, reply)) {
4652+ error("csd replied with an error: %s, %s",
4653+ err.name, err.message);
4654+ dbus_error_free(&err);
4655+ goto done;
4656+ }
4657+
4658+ dbus_message_iter_init(reply, &iter);
4659+
4660+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
4661+ error("Unexpected signature in Get return");
4662+ goto done;
4663+ }
4664+
4665+ dbus_message_iter_recurse(&iter, &sub);
4666+
4667+ if (g_strcmp0(prop, "RegistrationStatus") == 0) {
4668+ const char *status;
4669+
4670+ dbus_message_iter_get_basic(&sub, &status);
4671+ update_registration_status(status);
4672+
4673+ get_property(CSD_CSNET_OPERATOR, "OperatorName");
4674+ get_property(CSD_CSNET_SIGNAL, "SignalBars");
4675+ } else if (g_strcmp0(prop, "OperatorName") == 0) {
4676+ const char *name;
4677+
4678+ dbus_message_iter_get_basic(&sub, &name);
4679+ update_operator_name(name);
4680+ } else if (g_strcmp0(prop, "SignalBars") == 0) {
4681+ int32_t signal_bars;
4682+
4683+ dbus_message_iter_get_basic(&sub, &signal_bars);
4684+ update_signal_strength(signal_bars);
4685+ }
4686+
4687+done:
4688+ g_free(prop);
4689+ dbus_message_unref(reply);
4690+ remove_pending(call);
4691+}
4692+
4693+static int get_property(const char *iface, const char *prop)
4694+{
4695+ return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
4696+ DBUS_INTERFACE_PROPERTIES, "Get",
4697+ get_property_reply, g_strdup(prop),
4698+ DBUS_TYPE_STRING, &iface,
4699+ DBUS_TYPE_STRING, &prop,
4700+ DBUS_TYPE_INVALID);
4701+}
4702+
4703+static void handle_operator_name_changed(DBusMessage *msg)
4704+{
4705+ const char *name;
4706+
4707+ if (!dbus_message_get_args(msg, NULL,
4708+ DBUS_TYPE_STRING, &name,
4709+ DBUS_TYPE_INVALID)) {
4710+ error("Unexpected parameters in OperatorNameChanged");
4711+ return;
4712+ }
4713+
4714+ update_operator_name(name);
4715+}
4716+
4717+static void call_info_reply(DBusPendingCall *call, void *user_data)
4718+{
4719+ DBusError err;
4720+ DBusMessage *reply;
4721+ DBusMessageIter iter, sub;
4722+
4723+ get_calls_active = FALSE;
4724+
4725+ reply = dbus_pending_call_steal_reply(call);
4726+
4727+ dbus_error_init(&err);
4728+ if (dbus_set_error_from_message(&err, reply)) {
4729+ error("csd replied with an error: %s, %s",
4730+ err.name, err.message);
4731+ dbus_error_free(&err);
4732+ goto done;
4733+ }
4734+
4735+ dbus_message_iter_init(reply, &iter);
4736+
4737+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
4738+ error("Unexpected signature in GetCallInfoAll return");
4739+ goto done;
4740+ }
4741+
4742+ dbus_message_iter_recurse(&iter, &sub);
4743+
4744+ parse_call_list(&sub);
4745+
4746+ get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
4747+
4748+done:
4749+ dbus_message_unref(reply);
4750+ remove_pending(call);
4751+}
4752+
4753+
4754+static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
4755+{
4756+ DBusError derr;
4757+ DBusMessage *reply;
4758+ const char *name, *number, *secondname, *additionalnumber, *email;
4759+ int index;
4760+ char **number_type = user_data;
4761+
4762+ reply = dbus_pending_call_steal_reply(call);
4763+
4764+ dbus_error_init(&derr);
4765+ if (dbus_set_error_from_message(&derr, reply)) {
4766+ error("%s.ReadFirst replied with an error: %s, %s",
4767+ CSD_SIMPB_INTERFACE, derr.name, derr.message);
4768+ dbus_error_free(&derr);
4769+ if (number_type == &vmbx)
4770+ vmbx = g_strdup(getenv("VMBX_NUMBER"));
4771+ goto done;
4772+ }
4773+
4774+ dbus_error_init(&derr);
4775+ if (dbus_message_get_args(reply, NULL,
4776+ DBUS_TYPE_INT32, &index,
4777+ DBUS_TYPE_STRING, &name,
4778+ DBUS_TYPE_STRING, &number,
4779+ DBUS_TYPE_STRING, &secondname,
4780+ DBUS_TYPE_STRING, &additionalnumber,
4781+ DBUS_TYPE_STRING, &email,
4782+ DBUS_TYPE_INVALID) == FALSE) {
4783+ error("Unable to parse %s.ReadFirst arguments: %s, %s",
4784+ CSD_SIMPB_INTERFACE, derr.name, derr.message);
4785+ dbus_error_free(&derr);
4786+ goto done;
4787+ }
4788+
4789+ if (number_type == &msisdn) {
4790+ g_free(msisdn);
4791+ msisdn = g_strdup(number);
4792+ DBG("Got MSISDN %s (%s)", number, name);
4793+ } else {
4794+ g_free(vmbx);
4795+ vmbx = g_strdup(number);
4796+ DBG("Got voice mailbox number %s (%s)", number, name);
4797+ }
4798+
4799+done:
4800+ dbus_message_unref(reply);
4801+ remove_pending(call);
4802+}
4803+
4804+static void csd_init(void)
4805+{
4806+ const char *pb_type;
4807+ int ret;
4808+
4809+ ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
4810+ CSD_CALL_INTERFACE, "GetCallInfoAll",
4811+ call_info_reply, NULL, DBUS_TYPE_INVALID);
4812+ if (ret < 0) {
4813+ error("Unable to sent GetCallInfoAll method call");
4814+ return;
4815+ }
4816+
4817+ get_calls_active = TRUE;
4818+
4819+ pb_type = CSD_SIMPB_TYPE_MSISDN;
4820+
4821+ ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
4822+ CSD_SIMPB_INTERFACE, "ReadFirst",
4823+ phonebook_read_reply, &msisdn,
4824+ DBUS_TYPE_STRING, &pb_type,
4825+ DBUS_TYPE_INVALID);
4826+ if (ret < 0) {
4827+ error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
4828+ return;
4829+ }
4830+
4831+ /* Voicemail should be in MBDN index 0 */
4832+ pb_type = CSD_SIMPB_TYPE_MBDN;
4833+
4834+ ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
4835+ CSD_SIMPB_INTERFACE, "ReadFirst",
4836+ phonebook_read_reply, &vmbx,
4837+ DBUS_TYPE_STRING, &pb_type,
4838+ DBUS_TYPE_INVALID);
4839+ if (ret < 0) {
4840+ error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
4841+ return;
4842+ }
4843+}
4844+
4845+static void handle_modem_state(DBusMessage *msg)
4846+{
4847+ const char *state;
4848+
4849+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
4850+ DBUS_TYPE_INVALID)) {
4851+ error("Unexpected modem state parameters");
4852+ return;
4853+ }
4854+
4855+ DBG("SSC modem state: %s", state);
4856+
4857+ if (calls != NULL || get_calls_active)
4858+ return;
4859+
4860+ if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
4861+ csd_init();
4862+}
4863+
4864+static void modem_state_reply(DBusPendingCall *call, void *user_data)
4865+{
4866+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
4867+ DBusError err;
4868+
4869+ dbus_error_init(&err);
4870+ if (dbus_set_error_from_message(&err, reply)) {
4871+ error("get_modem_state: %s, %s", err.name, err.message);
4872+ dbus_error_free(&err);
4873+ } else
4874+ handle_modem_state(reply);
4875+
4876+ dbus_message_unref(reply);
4877+ remove_pending(call);
4878+}
4879+
4880+static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
4881+ void *data)
4882+{
4883+ const char *path = dbus_message_get_path(msg);
4884+
4885+ if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
4886+ handle_incoming_call(msg);
4887+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
4888+ handle_outgoing_call(msg);
4889+ else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
4890+ "CreateRequested"))
4891+ handle_create_requested(msg);
4892+ else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
4893+ handle_call_status(msg, path);
4894+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
4895+ handle_conference(msg, TRUE);
4896+ else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
4897+ handle_conference(msg, FALSE);
4898+ else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
4899+ "RegistrationChanged"))
4900+ handle_registration_changed(msg);
4901+ else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
4902+ "OperatorNameChanged"))
4903+ handle_operator_name_changed(msg);
4904+ else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
4905+ "SignalBarsChanged"))
4906+ handle_signal_bars_changed(msg);
4907+ else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
4908+ "PropertyModified"))
4909+ handle_hal_property_modified(msg);
4910+ else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
4911+ "modem_state_changed_ind"))
4912+ handle_modem_state(msg);
4913+
4914+ return TRUE;
4915+}
4916+
4917+static void add_watch(const char *sender, const char *path,
4918+ const char *interface, const char *member)
4919+{
4920+ guint watch;
4921+
4922+ watch = g_dbus_add_signal_watch(connection, sender, path, interface,
4923+ member, signal_filter, NULL, NULL);
4924+
4925+ watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
4926+}
4927+
4928+static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
4929+{
4930+ DBusError err;
4931+ DBusMessage *reply;
4932+ DBusMessageIter iter, sub;
4933+ const char *path;
4934+ int type;
4935+
4936+ reply = dbus_pending_call_steal_reply(call);
4937+
4938+ dbus_error_init(&err);
4939+ if (dbus_set_error_from_message(&err, reply)) {
4940+ error("hald replied with an error: %s, %s",
4941+ err.name, err.message);
4942+ dbus_error_free(&err);
4943+ goto done;
4944+ }
4945+
4946+ dbus_message_iter_init(reply, &iter);
4947+
4948+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
4949+ error("Unexpected signature in FindDeviceByCapability return");
4950+ goto done;
4951+ }
4952+
4953+ dbus_message_iter_recurse(&iter, &sub);
4954+
4955+ type = dbus_message_iter_get_arg_type(&sub);
4956+
4957+ if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
4958+ error("No hal device with battery capability found");
4959+ goto done;
4960+ }
4961+
4962+ dbus_message_iter_get_basic(&sub, &path);
4963+
4964+ DBG("telephony-maemo6: found battery device at %s", path);
4965+
4966+ add_watch(NULL, path, "org.freedesktop.Hal.Device",
4967+ "PropertyModified");
4968+
4969+ hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
4970+ hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
4971+ hal_get_integer(path, "battery.charge_level.design", &battchg_design);
4972+
4973+done:
4974+ dbus_message_unref(reply);
4975+ remove_pending(call);
4976+}
4977+
4978+int telephony_init(void)
4979+{
4980+ const char *battery_cap = "battery";
4981+ uint32_t features = AG_FEATURE_EC_ANDOR_NR |
4982+ AG_FEATURE_INBAND_RINGTONE |
4983+ AG_FEATURE_REJECT_A_CALL |
4984+ AG_FEATURE_ENHANCED_CALL_STATUS |
4985+ AG_FEATURE_ENHANCED_CALL_CONTROL |
4986+ AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
4987+ AG_FEATURE_THREE_WAY_CALLING;
4988+ int i;
4989+
4990+ DBG("");
4991+
4992+ connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
4993+
4994+ add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL);
4995+ add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL);
4996+ add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL);
4997+ add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged");
4998+ add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged");
4999+ add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged");
5000+ add_watch(NULL, NULL, SSC_DBUS_IFACE, "modem_state_changed_ind");
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: