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
=== added directory '.pc'
=== added file '.pc/.version'
--- .pc/.version 1970-01-01 00:00:00 +0000
+++ .pc/.version 2013-04-19 02:35:36 +0000
@@ -0,0 +1,1 @@
12
02
=== added directory '.pc/01_lower_sink_ranking.patch'
=== added directory '.pc/01_lower_sink_ranking.patch/audio'
=== added file '.pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c'
--- .pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c 1970-01-01 00:00:00 +0000
+++ .pc/01_lower_sink_ranking.patch/audio/gsta2dpsink.c 2013-04-19 02:35:36 +0000
@@ -0,0 +1,730 @@
1/*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
6 *
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 */
23
24#ifdef HAVE_CONFIG_H
25#include <config.h>
26#endif
27
28#include <unistd.h>
29#include <pthread.h>
30
31#include "gstpragma.h"
32#include "gsta2dpsink.h"
33
34GST_DEBUG_CATEGORY_STATIC(gst_a2dp_sink_debug);
35#define GST_CAT_DEFAULT gst_a2dp_sink_debug
36
37#define A2DP_SBC_RTP_PAYLOAD_TYPE 1
38#define TEMPLATE_MAX_BITPOOL_STR "64"
39
40#define DEFAULT_AUTOCONNECT TRUE
41
42enum {
43 PROP_0,
44 PROP_DEVICE,
45 PROP_AUTOCONNECT,
46 PROP_TRANSPORT
47};
48
49GST_BOILERPLATE(GstA2dpSink, gst_a2dp_sink, GstBin, GST_TYPE_BIN);
50
51static const GstElementDetails gst_a2dp_sink_details =
52 GST_ELEMENT_DETAILS("Bluetooth A2DP sink",
53 "Sink/Audio",
54 "Plays audio to an A2DP device",
55 "Marcel Holtmann <marcel@holtmann.org>");
56
57static GstStaticPadTemplate gst_a2dp_sink_factory =
58 GST_STATIC_PAD_TEMPLATE("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
59 GST_STATIC_CAPS("audio/x-sbc, "
60 "rate = (int) { 16000, 32000, 44100, 48000 }, "
61 "channels = (int) [ 1, 2 ], "
62 "mode = (string) { \"mono\", \"dual\", \"stereo\", \"joint\" }, "
63 "blocks = (int) { 4, 8, 12, 16 }, "
64 "subbands = (int) { 4, 8 }, "
65 "allocation = (string) { \"snr\", \"loudness\" }, "
66 "bitpool = (int) [ 2, "
67 TEMPLATE_MAX_BITPOOL_STR " ]; "
68 "audio/mpeg"
69 ));
70
71static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event);
72static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps);
73static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad);
74static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self);
75static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self);
76static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self);
77
78static void gst_a2dp_sink_finalize(GObject *obj)
79{
80 GstA2dpSink *self = GST_A2DP_SINK(obj);
81
82 g_mutex_free(self->cb_mutex);
83
84 G_OBJECT_CLASS(parent_class)->finalize(obj);
85}
86
87static GstState gst_a2dp_sink_get_state(GstA2dpSink *self)
88{
89 GstState current, pending;
90
91 gst_element_get_state(GST_ELEMENT(self), &current, &pending, 0);
92 if (pending == GST_STATE_VOID_PENDING)
93 return current;
94
95 return pending;
96}
97
98/*
99 * Helper function to create elements, add to the bin and link it
100 * to another element.
101 */
102static GstElement *gst_a2dp_sink_init_element(GstA2dpSink *self,
103 const gchar *elementname, const gchar *name,
104 GstElement *link_to)
105{
106 GstElement *element;
107 GstState state;
108
109 GST_LOG_OBJECT(self, "Initializing %s", elementname);
110
111 element = gst_element_factory_make(elementname, name);
112 if (element == NULL) {
113 GST_DEBUG_OBJECT(self, "Couldn't create %s", elementname);
114 return NULL;
115 }
116
117 if (!gst_bin_add(GST_BIN(self), element)) {
118 GST_DEBUG_OBJECT(self, "failed to add %s to the bin",
119 elementname);
120 goto cleanup_and_fail;
121 }
122
123 state = gst_a2dp_sink_get_state(self);
124 if (gst_element_set_state(element, state) ==
125 GST_STATE_CHANGE_FAILURE) {
126 GST_DEBUG_OBJECT(self, "%s failed to go to playing",
127 elementname);
128 goto remove_element_and_fail;
129 }
130
131 if (link_to != NULL)
132 if (!gst_element_link(link_to, element)) {
133 GST_DEBUG_OBJECT(self, "couldn't link %s",
134 elementname);
135 goto remove_element_and_fail;
136 }
137
138 return element;
139
140remove_element_and_fail:
141 gst_element_set_state(element, GST_STATE_NULL);
142 gst_bin_remove(GST_BIN(self), element);
143 return NULL;
144
145cleanup_and_fail:
146 g_object_unref(G_OBJECT(element));
147
148 return NULL;
149}
150
151static void gst_a2dp_sink_base_init(gpointer g_class)
152{
153 GstElementClass *element_class = GST_ELEMENT_CLASS(g_class);
154
155 gst_element_class_set_details(element_class,
156 &gst_a2dp_sink_details);
157 gst_element_class_add_pad_template(element_class,
158 gst_static_pad_template_get(&gst_a2dp_sink_factory));
159}
160
161static void gst_a2dp_sink_set_property(GObject *object, guint prop_id,
162 const GValue *value, GParamSpec *pspec)
163{
164 GstA2dpSink *self = GST_A2DP_SINK(object);
165
166 switch (prop_id) {
167 case PROP_DEVICE:
168 if (self->sink != NULL)
169 gst_avdtp_sink_set_device(self->sink,
170 g_value_get_string(value));
171
172 if (self->device != NULL)
173 g_free(self->device);
174 self->device = g_value_dup_string(value);
175 break;
176
177 case PROP_TRANSPORT:
178 if (self->sink != NULL)
179 gst_avdtp_sink_set_transport(self->sink,
180 g_value_get_string(value));
181
182 if (self->transport != NULL)
183 g_free(self->transport);
184 self->transport = g_value_dup_string(value);
185 break;
186
187 case PROP_AUTOCONNECT:
188 self->autoconnect = g_value_get_boolean(value);
189
190 if (self->sink != NULL)
191 g_object_set(G_OBJECT(self->sink), "auto-connect",
192 self->autoconnect, NULL);
193 break;
194
195 default:
196 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
197 break;
198 }
199}
200
201static void gst_a2dp_sink_get_property(GObject *object, guint prop_id,
202 GValue *value, GParamSpec *pspec)
203{
204 GstA2dpSink *self = GST_A2DP_SINK(object);
205 gchar *device, *transport;
206
207 switch (prop_id) {
208 case PROP_DEVICE:
209 if (self->sink != NULL) {
210 device = gst_avdtp_sink_get_device(self->sink);
211 if (device != NULL)
212 g_value_take_string(value, device);
213 }
214 break;
215 case PROP_AUTOCONNECT:
216 if (self->sink != NULL)
217 g_object_get(G_OBJECT(self->sink), "auto-connect",
218 &self->autoconnect, NULL);
219
220 g_value_set_boolean(value, self->autoconnect);
221 break;
222 case PROP_TRANSPORT:
223 if (self->sink != NULL) {
224 transport = gst_avdtp_sink_get_transport(self->sink);
225 if (transport != NULL)
226 g_value_take_string(value, transport);
227 }
228 break;
229 default:
230 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
231 break;
232 }
233}
234
235static gboolean gst_a2dp_sink_init_ghost_pad(GstA2dpSink *self)
236{
237 GstPad *capsfilter_pad;
238
239 /* we search for the capsfilter sinkpad */
240 capsfilter_pad = gst_element_get_static_pad(self->capsfilter, "sink");
241
242 /* now we add a ghostpad */
243 self->ghostpad = GST_GHOST_PAD(gst_ghost_pad_new("sink",
244 capsfilter_pad));
245 g_object_unref(capsfilter_pad);
246
247 /* the getcaps of our ghostpad must reflect the device caps */
248 gst_pad_set_getcaps_function(GST_PAD(self->ghostpad),
249 gst_a2dp_sink_get_caps);
250 self->ghostpad_setcapsfunc = GST_PAD_SETCAPSFUNC(self->ghostpad);
251 gst_pad_set_setcaps_function(GST_PAD(self->ghostpad),
252 GST_DEBUG_FUNCPTR(gst_a2dp_sink_set_caps));
253
254 /* we need to handle events on our own and we also need the eventfunc
255 * of the ghostpad for forwarding calls */
256 self->ghostpad_eventfunc = GST_PAD_EVENTFUNC(GST_PAD(self->ghostpad));
257 gst_pad_set_event_function(GST_PAD(self->ghostpad),
258 gst_a2dp_sink_handle_event);
259
260 if (!gst_element_add_pad(GST_ELEMENT(self), GST_PAD(self->ghostpad)))
261 GST_ERROR_OBJECT(self, "failed to add ghostpad");
262
263 return TRUE;
264}
265
266static void gst_a2dp_sink_remove_dynamic_elements(GstA2dpSink *self)
267{
268 if (self->rtp) {
269 GST_LOG_OBJECT(self, "removing rtp element from the bin");
270 if (!gst_bin_remove(GST_BIN(self), GST_ELEMENT(self->rtp)))
271 GST_WARNING_OBJECT(self, "failed to remove rtp "
272 "element from bin");
273 else
274 self->rtp = NULL;
275 }
276}
277
278static GstStateChangeReturn gst_a2dp_sink_change_state(GstElement *element,
279 GstStateChange transition)
280{
281 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
282 GstA2dpSink *self = GST_A2DP_SINK(element);
283
284 switch (transition) {
285 case GST_STATE_CHANGE_READY_TO_PAUSED:
286 self->taglist = gst_tag_list_new();
287
288 gst_a2dp_sink_init_fakesink(self);
289 break;
290
291 case GST_STATE_CHANGE_NULL_TO_READY:
292 self->sink_is_in_bin = FALSE;
293 self->sink = GST_AVDTP_SINK(gst_element_factory_make(
294 "avdtpsink", "avdtpsink"));
295 if (self->sink == NULL) {
296 GST_WARNING_OBJECT(self, "failed to create avdtpsink");
297 return GST_STATE_CHANGE_FAILURE;
298 }
299
300 if (self->device != NULL)
301 gst_avdtp_sink_set_device(self->sink,
302 self->device);
303
304 if (self->transport != NULL)
305 gst_avdtp_sink_set_transport(self->sink,
306 self->transport);
307
308 g_object_set(G_OBJECT(self->sink), "auto-connect",
309 self->autoconnect, NULL);
310
311 ret = gst_element_set_state(GST_ELEMENT(self->sink),
312 GST_STATE_READY);
313 break;
314 default:
315 break;
316 }
317
318 if (ret == GST_STATE_CHANGE_FAILURE)
319 return ret;
320
321 ret = GST_ELEMENT_CLASS(parent_class)->change_state(element,
322 transition);
323
324 switch (transition) {
325 case GST_STATE_CHANGE_PAUSED_TO_READY:
326 if (self->taglist) {
327 gst_tag_list_free(self->taglist);
328 self->taglist = NULL;
329 }
330 if (self->newseg_event != NULL) {
331 gst_event_unref(self->newseg_event);
332 self->newseg_event = NULL;
333 }
334 gst_a2dp_sink_remove_fakesink(self);
335 break;
336
337 case GST_STATE_CHANGE_READY_TO_NULL:
338 if (self->sink_is_in_bin) {
339 if (!gst_bin_remove(GST_BIN(self),
340 GST_ELEMENT(self->sink)))
341 GST_WARNING_OBJECT(self, "Failed to remove "
342 "avdtpsink from bin");
343 } else if (self->sink != NULL) {
344 gst_element_set_state(GST_ELEMENT(self->sink),
345 GST_STATE_NULL);
346 g_object_unref(G_OBJECT(self->sink));
347 }
348
349 self->sink = NULL;
350
351 gst_a2dp_sink_remove_dynamic_elements(self);
352 break;
353 default:
354 break;
355 }
356
357 return ret;
358}
359
360static void gst_a2dp_sink_class_init(GstA2dpSinkClass *klass)
361{
362 GObjectClass *object_class = G_OBJECT_CLASS(klass);
363 GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
364
365 parent_class = g_type_class_peek_parent(klass);
366
367 object_class->set_property = GST_DEBUG_FUNCPTR(
368 gst_a2dp_sink_set_property);
369 object_class->get_property = GST_DEBUG_FUNCPTR(
370 gst_a2dp_sink_get_property);
371
372 object_class->finalize = GST_DEBUG_FUNCPTR(
373 gst_a2dp_sink_finalize);
374
375 element_class->change_state = GST_DEBUG_FUNCPTR(
376 gst_a2dp_sink_change_state);
377
378 g_object_class_install_property(object_class, PROP_DEVICE,
379 g_param_spec_string("device", "Device",
380 "Bluetooth remote device address",
381 NULL, G_PARAM_READWRITE));
382
383 g_object_class_install_property(object_class, PROP_AUTOCONNECT,
384 g_param_spec_boolean("auto-connect", "Auto-connect",
385 "Automatically attempt to connect to device",
386 DEFAULT_AUTOCONNECT, G_PARAM_READWRITE));
387
388 g_object_class_install_property(object_class, PROP_TRANSPORT,
389 g_param_spec_string("transport", "Transport",
390 "Use configured transport",
391 NULL, G_PARAM_READWRITE));
392
393 GST_DEBUG_CATEGORY_INIT(gst_a2dp_sink_debug, "a2dpsink", 0,
394 "A2DP sink element");
395}
396
397GstCaps *gst_a2dp_sink_get_device_caps(GstA2dpSink *self)
398{
399 return gst_avdtp_sink_get_device_caps(self->sink);
400}
401
402static GstCaps *gst_a2dp_sink_get_caps(GstPad *pad)
403{
404 GstCaps *caps;
405 GstCaps *caps_aux;
406 GstA2dpSink *self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
407
408 if (self->sink == NULL) {
409 GST_DEBUG_OBJECT(self, "a2dpsink isn't initialized "
410 "returning template caps");
411 caps = gst_static_pad_template_get_caps(
412 &gst_a2dp_sink_factory);
413 } else {
414 GST_LOG_OBJECT(self, "Getting device caps");
415 caps = gst_a2dp_sink_get_device_caps(self);
416 if (caps == NULL)
417 caps = gst_static_pad_template_get_caps(
418 &gst_a2dp_sink_factory);
419 }
420 caps_aux = gst_caps_copy(caps);
421 g_object_set(self->capsfilter, "caps", caps_aux, NULL);
422 gst_caps_unref(caps_aux);
423 return caps;
424}
425
426static gboolean gst_a2dp_sink_init_avdtp_sink(GstA2dpSink *self)
427{
428 GstElement *sink;
429
430 /* check if we don't need a new sink */
431 if (self->sink_is_in_bin)
432 return TRUE;
433
434 if (self->sink == NULL)
435 sink = gst_element_factory_make("avdtpsink", "avdtpsink");
436 else
437 sink = GST_ELEMENT(self->sink);
438
439 if (sink == NULL) {
440 GST_ERROR_OBJECT(self, "Couldn't create avdtpsink");
441 return FALSE;
442 }
443
444 if (!gst_bin_add(GST_BIN(self), sink)) {
445 GST_ERROR_OBJECT(self, "failed to add avdtpsink "
446 "to the bin");
447 goto cleanup_and_fail;
448 }
449
450 if (gst_element_set_state(sink, GST_STATE_READY) ==
451 GST_STATE_CHANGE_FAILURE) {
452 GST_ERROR_OBJECT(self, "avdtpsink failed to go to ready");
453 goto remove_element_and_fail;
454 }
455
456 if (!gst_element_link(GST_ELEMENT(self->rtp), sink)) {
457 GST_ERROR_OBJECT(self, "couldn't link rtpsbcpay "
458 "to avdtpsink");
459 goto remove_element_and_fail;
460 }
461
462 self->sink = GST_AVDTP_SINK(sink);
463 self->sink_is_in_bin = TRUE;
464 g_object_set(G_OBJECT(self->sink), "device", self->device, NULL);
465 g_object_set(G_OBJECT(self->sink), "transport", self->transport, NULL);
466
467 gst_element_set_state(sink, GST_STATE_PAUSED);
468
469 return TRUE;
470
471remove_element_and_fail:
472 gst_element_set_state(sink, GST_STATE_NULL);
473 gst_bin_remove(GST_BIN(self), sink);
474 return FALSE;
475
476cleanup_and_fail:
477 if (sink != NULL)
478 g_object_unref(G_OBJECT(sink));
479
480 return FALSE;
481}
482
483static gboolean gst_a2dp_sink_init_rtp_sbc_element(GstA2dpSink *self)
484{
485 GstElement *rtppay;
486
487 /* if we already have a rtp, we don't need a new one */
488 if (self->rtp != NULL)
489 return TRUE;
490
491 rtppay = gst_a2dp_sink_init_element(self, "rtpsbcpay", "rtp",
492 self->capsfilter);
493 if (rtppay == NULL)
494 return FALSE;
495
496 self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
497 g_object_set(G_OBJECT(self->rtp), "min-frames", -1, NULL);
498
499 gst_element_set_state(rtppay, GST_STATE_PAUSED);
500
501 return TRUE;
502}
503
504static gboolean gst_a2dp_sink_init_rtp_mpeg_element(GstA2dpSink *self)
505{
506 GstElement *rtppay;
507
508 /* check if we don't need a new rtp */
509 if (self->rtp)
510 return TRUE;
511
512 GST_LOG_OBJECT(self, "Initializing rtp mpeg element");
513 /* if capsfilter is not created then we can't have our rtp element */
514 if (self->capsfilter == NULL)
515 return FALSE;
516
517 rtppay = gst_a2dp_sink_init_element(self, "rtpmpapay", "rtp",
518 self->capsfilter);
519 if (rtppay == NULL)
520 return FALSE;
521
522 self->rtp = GST_BASE_RTP_PAYLOAD(rtppay);
523
524 gst_element_set_state(rtppay, GST_STATE_PAUSED);
525
526 return TRUE;
527}
528
529static gboolean gst_a2dp_sink_init_dynamic_elements(GstA2dpSink *self,
530 GstCaps *caps)
531{
532 GstStructure *structure;
533 GstEvent *event;
534 GstPad *capsfilterpad;
535 gboolean crc;
536 gchar *mode = NULL;
537
538 structure = gst_caps_get_structure(caps, 0);
539
540 /* before everything we need to remove fakesink */
541 gst_a2dp_sink_remove_fakesink(self);
542
543 /* first, we need to create our rtp payloader */
544 if (gst_structure_has_name(structure, "audio/x-sbc")) {
545 GST_LOG_OBJECT(self, "sbc media received");
546 if (!gst_a2dp_sink_init_rtp_sbc_element(self))
547 return FALSE;
548 } else if (gst_structure_has_name(structure, "audio/mpeg")) {
549 GST_LOG_OBJECT(self, "mp3 media received");
550 if (!gst_a2dp_sink_init_rtp_mpeg_element(self))
551 return FALSE;
552 } else {
553 GST_ERROR_OBJECT(self, "Unexpected media type");
554 return FALSE;
555 }
556
557 if (!gst_a2dp_sink_init_avdtp_sink(self))
558 return FALSE;
559
560 /* check if we should push the taglist FIXME should we push this?
561 * we can send the tags directly if needed */
562 if (self->taglist != NULL &&
563 gst_structure_has_name(structure, "audio/mpeg")) {
564
565 event = gst_event_new_tag(self->taglist);
566
567 /* send directly the crc */
568 if (gst_tag_list_get_boolean(self->taglist, "has-crc", &crc))
569 gst_avdtp_sink_set_crc(self->sink, crc);
570
571 if (gst_tag_list_get_string(self->taglist, "channel-mode",
572 &mode))
573 gst_avdtp_sink_set_channel_mode(self->sink, mode);
574
575 capsfilterpad = gst_ghost_pad_get_target(self->ghostpad);
576 gst_pad_send_event(capsfilterpad, event);
577 self->taglist = NULL;
578 g_free(mode);
579 }
580
581 if (!gst_avdtp_sink_set_device_caps(self->sink, caps))
582 return FALSE;
583
584 g_object_set(G_OBJECT(self->rtp), "mtu",
585 gst_avdtp_sink_get_link_mtu(self->sink), NULL);
586
587 /* we forward our new segment here if we have one */
588 if (self->newseg_event) {
589 gst_pad_send_event(GST_BASE_RTP_PAYLOAD_SINKPAD(self->rtp),
590 self->newseg_event);
591 self->newseg_event = NULL;
592 }
593
594 return TRUE;
595}
596
597static gboolean gst_a2dp_sink_set_caps(GstPad *pad, GstCaps *caps)
598{
599 GstA2dpSink *self;
600
601 self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
602 GST_INFO_OBJECT(self, "setting caps");
603
604 /* now we know the caps */
605 gst_a2dp_sink_init_dynamic_elements(self, caps);
606
607 return self->ghostpad_setcapsfunc(GST_PAD(self->ghostpad), caps);
608}
609
610/* used for catching newsegment events while we don't have a sink, for
611 * later forwarding it to the sink */
612static gboolean gst_a2dp_sink_handle_event(GstPad *pad, GstEvent *event)
613{
614 GstA2dpSink *self;
615 GstTagList *taglist = NULL;
616 GstObject *parent;
617
618 self = GST_A2DP_SINK(GST_PAD_PARENT(pad));
619 parent = gst_element_get_parent(GST_ELEMENT(self->sink));
620
621 if (GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT &&
622 parent != GST_OBJECT_CAST(self)) {
623 if (self->newseg_event != NULL)
624 gst_event_unref(self->newseg_event);
625 self->newseg_event = gst_event_ref(event);
626
627 } else if (GST_EVENT_TYPE(event) == GST_EVENT_TAG &&
628 parent != GST_OBJECT_CAST(self)) {
629 if (self->taglist == NULL)
630 gst_event_parse_tag(event, &self->taglist);
631 else {
632 gst_event_parse_tag(event, &taglist);
633 gst_tag_list_insert(self->taglist, taglist,
634 GST_TAG_MERGE_REPLACE);
635 }
636 }
637
638 if (parent != NULL)
639 gst_object_unref(GST_OBJECT(parent));
640
641 return self->ghostpad_eventfunc(GST_PAD(self->ghostpad), event);
642}
643
644static gboolean gst_a2dp_sink_init_caps_filter(GstA2dpSink *self)
645{
646 GstElement *element;
647
648 element = gst_element_factory_make("capsfilter", "filter");
649 if (element == NULL)
650 goto failed;
651
652 if (!gst_bin_add(GST_BIN(self), element))
653 goto failed;
654
655 self->capsfilter = element;
656 return TRUE;
657
658failed:
659 GST_ERROR_OBJECT(self, "Failed to initialize caps filter");
660 return FALSE;
661}
662
663static gboolean gst_a2dp_sink_init_fakesink(GstA2dpSink *self)
664{
665 if (self->fakesink != NULL)
666 return TRUE;
667
668 g_mutex_lock(self->cb_mutex);
669 self->fakesink = gst_a2dp_sink_init_element(self, "fakesink",
670 "fakesink", self->capsfilter);
671 g_mutex_unlock(self->cb_mutex);
672
673 if (!self->fakesink)
674 return FALSE;
675
676 return TRUE;
677}
678
679static gboolean gst_a2dp_sink_remove_fakesink(GstA2dpSink *self)
680{
681 g_mutex_lock(self->cb_mutex);
682
683 if (self->fakesink != NULL) {
684 gst_element_set_locked_state(self->fakesink, TRUE);
685 gst_element_set_state(self->fakesink, GST_STATE_NULL);
686
687 gst_bin_remove(GST_BIN(self), self->fakesink);
688 self->fakesink = NULL;
689 }
690
691 g_mutex_unlock(self->cb_mutex);
692
693 return TRUE;
694}
695
696static void gst_a2dp_sink_init(GstA2dpSink *self,
697 GstA2dpSinkClass *klass)
698{
699 self->sink = NULL;
700 self->fakesink = NULL;
701 self->rtp = NULL;
702 self->device = NULL;
703 self->transport = NULL;
704 self->autoconnect = DEFAULT_AUTOCONNECT;
705 self->capsfilter = NULL;
706 self->newseg_event = NULL;
707 self->taglist = NULL;
708 self->ghostpad = NULL;
709 self->sink_is_in_bin = FALSE;
710
711 self->cb_mutex = g_mutex_new();
712
713 /* we initialize our capsfilter */
714 gst_a2dp_sink_init_caps_filter(self);
715 g_object_set(self->capsfilter, "caps",
716 gst_static_pad_template_get_caps(&gst_a2dp_sink_factory),
717 NULL);
718
719 gst_a2dp_sink_init_fakesink(self);
720
721 gst_a2dp_sink_init_ghost_pad(self);
722
723}
724
725gboolean gst_a2dp_sink_plugin_init(GstPlugin *plugin)
726{
727 return gst_element_register(plugin, "a2dpsink",
728 GST_RANK_MARGINAL, GST_TYPE_A2DP_SINK);
729}
730
0731
=== added directory '.pc/02_disable_hal.patch'
=== added directory '.pc/02_disable_hal.patch/audio'
=== added file '.pc/02_disable_hal.patch/audio/telephony-maemo5.c'
--- .pc/02_disable_hal.patch/audio/telephony-maemo5.c 1970-01-01 00:00:00 +0000
+++ .pc/02_disable_hal.patch/audio/telephony-maemo5.c 2013-04-19 02:35:36 +0000
@@ -0,0 +1,2104 @@
1/*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2008-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdlib.h>
30#include <stdio.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <stdint.h>
34#include <string.h>
35#include <glib.h>
36#include <dbus/dbus.h>
37#include <gdbus.h>
38
39#include "log.h"
40#include "telephony.h"
41#include "error.h"
42
43/* SSC D-Bus definitions */
44#define SSC_DBUS_NAME "com.nokia.phone.SSC"
45#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
46#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
47
48/* libcsnet D-Bus definitions */
49#define NETWORK_BUS_NAME "com.nokia.phone.net"
50#define NETWORK_INTERFACE "Phone.Net"
51#define NETWORK_PATH "/com/nokia/phone/net"
52
53/* Mask bits for supported services */
54#define NETWORK_MASK_GPRS_SUPPORT 0x01
55#define NETWORK_MASK_CS_SERVICES 0x02
56#define NETWORK_MASK_EGPRS_SUPPORT 0x04
57#define NETWORK_MASK_HSDPA_AVAIL 0x08
58#define NETWORK_MASK_HSUPA_AVAIL 0x10
59
60/* network get cell info: cell type */
61#define NETWORK_UNKNOWN_CELL 0
62#define NETWORK_GSM_CELL 1
63#define NETWORK_WCDMA_CELL 2
64
65enum net_registration_status {
66 NETWORK_REG_STATUS_HOME = 0x00,
67 NETWORK_REG_STATUS_ROAM,
68 NETWORK_REG_STATUS_ROAM_BLINK,
69 NETWORK_REG_STATUS_NOSERV,
70 NETWORK_REG_STATUS_NOSERV_SEARCHING,
71 NETWORK_REG_STATUS_NOSERV_NOTSEARCHING,
72 NETWORK_REG_STATUS_NOSERV_NOSIM,
73 NETWORK_REG_STATUS_POWER_OFF = 0x08,
74 NETWORK_REG_STATUS_NSPS,
75 NETWORK_REG_STATUS_NSPS_NO_COVERAGE,
76 NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW
77};
78
79enum network_types {
80 NETWORK_GSM_HOME_PLMN = 0,
81 NETWORK_GSM_PREFERRED_PLMN,
82 NETWORK_GSM_FORBIDDEN_PLMN,
83 NETWORK_GSM_OTHER_PLMN,
84 NETWORK_GSM_NO_PLMN_AVAIL
85};
86
87enum network_alpha_tag_name_type {
88 NETWORK_HARDCODED_LATIN_OPER_NAME = 0,
89 NETWORK_HARDCODED_USC2_OPER_NAME,
90 NETWORK_NITZ_SHORT_OPER_NAME,
91 NETWORK_NITZ_FULL_OPER_NAME,
92};
93
94#define TELEPHONY_MAEMO_PATH "/com/nokia/MaemoTelephony"
95#define TELEPHONY_MAEMO_INTERFACE "com.nokia.MaemoTelephony"
96
97#define CALLERID_BASE "/var/lib/bluetooth/maemo-callerid-"
98#define ALLOWED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-allowed"
99#define RESTRICTED_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-restricted"
100#define NONE_FLAG_FILE "/var/lib/bluetooth/maemo-callerid-none"
101
102static uint32_t callerid = 0;
103
104/* CSD CALL plugin D-Bus definitions */
105#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
106#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
107#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
108#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
109#define CSD_CALL_PATH "/com/nokia/csd/call"
110#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
111
112/* Call status values as exported by the CSD CALL plugin */
113#define CSD_CALL_STATUS_IDLE 0
114#define CSD_CALL_STATUS_CREATE 1
115#define CSD_CALL_STATUS_COMING 2
116#define CSD_CALL_STATUS_PROCEEDING 3
117#define CSD_CALL_STATUS_MO_ALERTING 4
118#define CSD_CALL_STATUS_MT_ALERTING 5
119#define CSD_CALL_STATUS_WAITING 6
120#define CSD_CALL_STATUS_ANSWERED 7
121#define CSD_CALL_STATUS_ACTIVE 8
122#define CSD_CALL_STATUS_MO_RELEASE 9
123#define CSD_CALL_STATUS_MT_RELEASE 10
124#define CSD_CALL_STATUS_HOLD_INITIATED 11
125#define CSD_CALL_STATUS_HOLD 12
126#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
127#define CSD_CALL_STATUS_RECONNECT_PENDING 14
128#define CSD_CALL_STATUS_TERMINATED 15
129#define CSD_CALL_STATUS_SWAP_INITIATED 16
130
131#define CALL_FLAG_NONE 0
132#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
133#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
134
135/* SIM Phonebook D-Bus definitions */
136#define SIM_PHONEBOOK_BUS_NAME "com.nokia.phone.SIM"
137#define SIM_PHONEBOOK_INTERFACE "Phone.Sim.Phonebook"
138#define SIM_PHONEBOOK_PATH "/com/nokia/phone/SIM/phonebook"
139
140#define PHONEBOOK_INDEX_FIRST_ENTRY 0xFFFF
141#define PHONEBOOK_INDEX_NEXT_FREE_LOCATION 0xFFFE
142
143enum sim_phonebook_type {
144 SIM_PHONEBOOK_TYPE_ADN = 0x0,
145 SIM_PHONEBOOK_TYPE_SDN,
146 SIM_PHONEBOOK_TYPE_FDN,
147 SIM_PHONEBOOK_TYPE_VMBX,
148 SIM_PHONEBOOK_TYPE_MBDN,
149 SIM_PHONEBOOK_TYPE_EN,
150 SIM_PHONEBOOK_TYPE_MSISDN
151};
152
153enum sim_phonebook_location_type {
154 SIM_PHONEBOOK_LOCATION_EXACT = 0x0,
155 SIM_PHONEBOOK_LOCATION_NEXT
156};
157
158struct csd_call {
159 char *object_path;
160 int status;
161 gboolean originating;
162 gboolean emergency;
163 gboolean on_hold;
164 gboolean conference;
165 char *number;
166 gboolean setup;
167};
168
169static struct {
170 uint8_t status;
171 uint16_t lac;
172 uint32_t cell_id;
173 uint32_t operator_code;
174 uint32_t country_code;
175 uint8_t network_type;
176 uint8_t supported_services;
177 uint16_t signals_bar;
178 char *operator_name;
179} net = {
180 .status = NETWORK_REG_STATUS_NOSERV,
181 .lac = 0,
182 .cell_id = 0,
183 .operator_code = 0,
184 .country_code = 0,
185 .network_type = NETWORK_GSM_NO_PLMN_AVAIL,
186 .supported_services = 0,
187 .signals_bar = 0,
188 .operator_name = NULL,
189};
190
191static DBusConnection *connection = NULL;
192
193static GSList *calls = NULL;
194
195/* Reference count for determining the call indicator status */
196static GSList *active_calls = NULL;
197
198static char *msisdn = NULL; /* Subscriber number */
199static char *vmbx = NULL; /* Voice mailbox number */
200
201/* HAL battery namespace key values */
202static int battchg_cur = -1; /* "battery.charge_level.current" */
203static int battchg_last = -1; /* "battery.charge_level.last_full" */
204static int battchg_design = -1; /* "battery.charge_level.design" */
205
206static gboolean get_calls_active = FALSE;
207
208static gboolean events_enabled = FALSE;
209
210/* Supported set of call hold operations */
211static const char *chld_str = "0,1,1x,2,2x,3,4";
212
213static char *last_dialed_number = NULL;
214
215/* Timer for tracking call creation requests */
216static guint create_request_timer = 0;
217
218static struct indicator maemo_indicators[] =
219{
220 { "battchg", "0-5", 5, TRUE },
221 { "signal", "0-5", 0, TRUE },
222 { "service", "0,1", 0, TRUE },
223 { "call", "0,1", 0, TRUE },
224 { "callsetup", "0-3", 0, TRUE },
225 { "callheld", "0-2", 0, FALSE },
226 { "roam", "0,1", 0, TRUE },
227 { NULL }
228};
229
230static char *call_status_str[] = {
231 "IDLE",
232 "CREATE",
233 "COMING",
234 "PROCEEDING",
235 "MO_ALERTING",
236 "MT_ALERTING",
237 "WAITING",
238 "ANSWERED",
239 "ACTIVE",
240 "MO_RELEASE",
241 "MT_RELEASE",
242 "HOLD_INITIATED",
243 "HOLD",
244 "RETRIEVE_INITIATED",
245 "RECONNECT_PENDING",
246 "TERMINATED",
247 "SWAP_INITIATED",
248 "???"
249};
250
251static struct csd_call *find_call(const char *path)
252{
253 GSList *l;
254
255 for (l = calls; l != NULL; l = l->next) {
256 struct csd_call *call = l->data;
257
258 if (g_str_equal(call->object_path, path))
259 return call;
260 }
261
262 return NULL;
263}
264
265static struct csd_call *find_non_held_call(void)
266{
267 GSList *l;
268
269 for (l = calls; l != NULL; l = l->next) {
270 struct csd_call *call = l->data;
271
272 if (call->status == CSD_CALL_STATUS_IDLE)
273 continue;
274
275 if (call->status != CSD_CALL_STATUS_HOLD)
276 return call;
277 }
278
279 return NULL;
280}
281
282static struct csd_call *find_non_idle_call(void)
283{
284 GSList *l;
285
286 for (l = calls; l != NULL; l = l->next) {
287 struct csd_call *call = l->data;
288
289 if (call->status != CSD_CALL_STATUS_IDLE)
290 return call;
291 }
292
293 return NULL;
294}
295
296static struct csd_call *find_call_with_status(int status)
297{
298 GSList *l;
299
300 for (l = calls; l != NULL; l = l->next) {
301 struct csd_call *call = l->data;
302
303 if (call->status == status)
304 return call;
305 }
306
307 return NULL;
308}
309
310static int release_conference(void)
311{
312 DBusMessage *msg;
313
314 DBG("telephony-maemo: releasing conference call");
315
316 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
317 CSD_CALL_CONFERENCE_PATH,
318 CSD_CALL_INSTANCE,
319 "Release");
320 if (!msg) {
321 error("Unable to allocate new D-Bus message");
322 return -ENOMEM;
323 }
324
325 g_dbus_send_message(connection, msg);
326
327 return 0;
328}
329
330static int release_call(struct csd_call *call)
331{
332 DBusMessage *msg;
333
334 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
335 call->object_path,
336 CSD_CALL_INSTANCE,
337 "Release");
338 if (!msg) {
339 error("Unable to allocate new D-Bus message");
340 return -ENOMEM;
341 }
342
343 g_dbus_send_message(connection, msg);
344
345 return 0;
346}
347
348static int answer_call(struct csd_call *call)
349{
350 DBusMessage *msg;
351
352 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
353 call->object_path,
354 CSD_CALL_INSTANCE,
355 "Answer");
356 if (!msg) {
357 error("Unable to allocate new D-Bus message");
358 return -ENOMEM;
359 }
360
361 g_dbus_send_message(connection, msg);
362
363 return 0;
364}
365
366static int split_call(struct csd_call *call)
367{
368 DBusMessage *msg;
369
370 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
371 call->object_path,
372 CSD_CALL_INSTANCE,
373 "Split");
374 if (!msg) {
375 error("Unable to allocate new D-Bus message");
376 return -ENOMEM;
377 }
378
379 g_dbus_send_message(connection, msg);
380
381 return 0;
382}
383
384static int unhold_call(struct csd_call *call)
385{
386 DBusMessage *msg;
387
388 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
389 CSD_CALL_INTERFACE,
390 "Unhold");
391 if (!msg) {
392 error("Unable to allocate new D-Bus message");
393 return -ENOMEM;
394 }
395
396 g_dbus_send_message(connection, msg);
397
398 return 0;
399}
400
401static int hold_call(struct csd_call *call)
402{
403 DBusMessage *msg;
404
405 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
406 CSD_CALL_INTERFACE,
407 "Hold");
408 if (!msg) {
409 error("Unable to allocate new D-Bus message");
410 return -ENOMEM;
411 }
412
413 g_dbus_send_message(connection, msg);
414
415 return 0;
416}
417
418static int swap_calls(void)
419{
420 DBusMessage *msg;
421
422 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
423 CSD_CALL_INTERFACE,
424 "Swap");
425 if (!msg) {
426 error("Unable to allocate new D-Bus message");
427 return -ENOMEM;
428 }
429
430 g_dbus_send_message(connection, msg);
431
432 return 0;
433}
434
435static int create_conference(void)
436{
437 DBusMessage *msg;
438
439 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
440 CSD_CALL_INTERFACE,
441 "Conference");
442 if (!msg) {
443 error("Unable to allocate new D-Bus message");
444 return -ENOMEM;
445 }
446
447 g_dbus_send_message(connection, msg);
448
449 return 0;
450}
451
452static int call_transfer(void)
453{
454 DBusMessage *msg;
455
456 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
457 CSD_CALL_INTERFACE,
458 "Transfer");
459 if (!msg) {
460 error("Unable to allocate new D-Bus message");
461 return -ENOMEM;
462 }
463
464 g_dbus_send_message(connection, msg);
465
466 return 0;
467}
468
469static int number_type(const char *number)
470{
471 if (number == NULL)
472 return NUMBER_TYPE_TELEPHONY;
473
474 if (number[0] == '+' || strncmp(number, "00", 2) == 0)
475 return NUMBER_TYPE_INTERNATIONAL;
476
477 return NUMBER_TYPE_TELEPHONY;
478}
479
480void telephony_device_connected(void *telephony_device)
481{
482 struct csd_call *coming;
483
484 DBG("telephony-maemo: device %p connected", telephony_device);
485
486 coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
487 if (coming) {
488 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
489 telephony_call_waiting_ind(coming->number,
490 number_type(coming->number));
491 else
492 telephony_incoming_call_ind(coming->number,
493 number_type(coming->number));
494 }
495}
496
497void telephony_device_disconnected(void *telephony_device)
498{
499 DBG("telephony-maemo: device %p disconnected", telephony_device);
500 events_enabled = FALSE;
501}
502
503void telephony_event_reporting_req(void *telephony_device, int ind)
504{
505 events_enabled = ind == 1 ? TRUE : FALSE;
506
507 telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
508}
509
510void telephony_response_and_hold_req(void *telephony_device, int rh)
511{
512 telephony_response_and_hold_rsp(telephony_device,
513 CME_ERROR_NOT_SUPPORTED);
514}
515
516void telephony_last_dialed_number_req(void *telephony_device)
517{
518 DBG("telephony-maemo: last dialed number request");
519
520 if (last_dialed_number)
521 telephony_dial_number_req(telephony_device,
522 last_dialed_number);
523 else
524 telephony_last_dialed_number_rsp(telephony_device,
525 CME_ERROR_NOT_ALLOWED);
526}
527
528void telephony_terminate_call_req(void *telephony_device)
529{
530 struct csd_call *call;
531 int err;
532
533 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
534 if (!call)
535 call = find_non_idle_call();
536
537 if (!call) {
538 error("No active call");
539 telephony_terminate_call_rsp(telephony_device,
540 CME_ERROR_NOT_ALLOWED);
541 return;
542 }
543
544 if (call->conference)
545 err = release_conference();
546 else
547 err = release_call(call);
548
549 if (err < 0)
550 telephony_terminate_call_rsp(telephony_device,
551 CME_ERROR_AG_FAILURE);
552 else
553 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
554}
555
556void telephony_answer_call_req(void *telephony_device)
557{
558 struct csd_call *call;
559
560 call = find_call_with_status(CSD_CALL_STATUS_COMING);
561 if (!call)
562 call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
563
564 if (!call)
565 call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
566
567 if (!call)
568 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
569
570 if (!call) {
571 telephony_answer_call_rsp(telephony_device,
572 CME_ERROR_NOT_ALLOWED);
573 return;
574 }
575
576 if (answer_call(call) < 0)
577 telephony_answer_call_rsp(telephony_device,
578 CME_ERROR_AG_FAILURE);
579 else
580 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
581}
582
583static int send_method_call(const char *dest, const char *path,
584 const char *interface, const char *method,
585 DBusPendingCallNotifyFunction cb,
586 void *user_data, int type, ...)
587{
588 DBusMessage *msg;
589 DBusPendingCall *call;
590 va_list args;
591
592 msg = dbus_message_new_method_call(dest, path, interface, method);
593 if (!msg) {
594 error("Unable to allocate new D-Bus %s message", method);
595 return -ENOMEM;
596 }
597
598 va_start(args, type);
599
600 if (!dbus_message_append_args_valist(msg, type, args)) {
601 dbus_message_unref(msg);
602 va_end(args);
603 return -EIO;
604 }
605
606 va_end(args);
607
608 if (!cb) {
609 g_dbus_send_message(connection, msg);
610 return 0;
611 }
612
613 if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
614 error("Sending %s failed", method);
615 dbus_message_unref(msg);
616 return -EIO;
617 }
618
619 dbus_pending_call_set_notify(call, cb, user_data, NULL);
620 dbus_pending_call_unref(call);
621 dbus_message_unref(msg);
622
623 return 0;
624}
625
626static const char *memory_dial_lookup(int location)
627{
628 if (location == 1)
629 return vmbx;
630 else
631 return NULL;
632}
633
634void telephony_dial_number_req(void *telephony_device, const char *number)
635{
636 uint32_t flags = callerid;
637 int ret;
638
639 DBG("telephony-maemo: dial request to %s", number);
640
641 if (strncmp(number, "*31#", 4) == 0) {
642 number += 4;
643 flags = CALL_FLAG_PRESENTATION_ALLOWED;
644 } else if (strncmp(number, "#31#", 4) == 0) {
645 number += 4;
646 flags = CALL_FLAG_PRESENTATION_RESTRICTED;
647 } else if (number[0] == '>') {
648 const char *location = &number[1];
649
650 number = memory_dial_lookup(strtol(&number[1], NULL, 0));
651 if (!number) {
652 error("No number at memory location %s", location);
653 telephony_dial_number_rsp(telephony_device,
654 CME_ERROR_INVALID_INDEX);
655 return;
656 }
657 }
658
659 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
660 CSD_CALL_INTERFACE, "CreateWith",
661 NULL, NULL,
662 DBUS_TYPE_STRING, &number,
663 DBUS_TYPE_UINT32, &flags,
664 DBUS_TYPE_INVALID);
665 if (ret < 0) {
666 telephony_dial_number_rsp(telephony_device,
667 CME_ERROR_AG_FAILURE);
668 return;
669 }
670
671 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
672}
673
674void telephony_transmit_dtmf_req(void *telephony_device, char tone)
675{
676 int ret;
677 char buf[2] = { tone, '\0' }, *buf_ptr = buf;
678
679 DBG("telephony-maemo: transmit dtmf: %s", buf);
680
681 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
682 CSD_CALL_INTERFACE, "SendDTMF",
683 NULL, NULL,
684 DBUS_TYPE_STRING, &buf_ptr,
685 DBUS_TYPE_INVALID);
686 if (ret < 0) {
687 telephony_transmit_dtmf_rsp(telephony_device,
688 CME_ERROR_AG_FAILURE);
689 return;
690 }
691
692 telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
693}
694
695void telephony_subscriber_number_req(void *telephony_device)
696{
697 DBG("telephony-maemo: subscriber number request");
698 if (msisdn)
699 telephony_subscriber_number_ind(msisdn,
700 number_type(msisdn),
701 SUBSCRIBER_SERVICE_VOICE);
702 telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
703}
704
705static int csd_status_to_hfp(struct csd_call *call)
706{
707 switch (call->status) {
708 case CSD_CALL_STATUS_IDLE:
709 case CSD_CALL_STATUS_MO_RELEASE:
710 case CSD_CALL_STATUS_MT_RELEASE:
711 case CSD_CALL_STATUS_TERMINATED:
712 return -1;
713 case CSD_CALL_STATUS_CREATE:
714 return CALL_STATUS_DIALING;
715 case CSD_CALL_STATUS_WAITING:
716 return CALL_STATUS_WAITING;
717 case CSD_CALL_STATUS_PROCEEDING:
718 /* PROCEEDING can happen in outgoing/incoming */
719 if (call->originating)
720 return CALL_STATUS_DIALING;
721 else
722 return CALL_STATUS_INCOMING;
723 case CSD_CALL_STATUS_COMING:
724 return CALL_STATUS_INCOMING;
725 case CSD_CALL_STATUS_MO_ALERTING:
726 return CALL_STATUS_ALERTING;
727 case CSD_CALL_STATUS_MT_ALERTING:
728 return CALL_STATUS_INCOMING;
729 case CSD_CALL_STATUS_ANSWERED:
730 case CSD_CALL_STATUS_ACTIVE:
731 case CSD_CALL_STATUS_RECONNECT_PENDING:
732 case CSD_CALL_STATUS_SWAP_INITIATED:
733 case CSD_CALL_STATUS_HOLD_INITIATED:
734 return CALL_STATUS_ACTIVE;
735 case CSD_CALL_STATUS_RETRIEVE_INITIATED:
736 case CSD_CALL_STATUS_HOLD:
737 return CALL_STATUS_HELD;
738 default:
739 return -1;
740 }
741}
742
743void telephony_list_current_calls_req(void *telephony_device)
744{
745 GSList *l;
746 int i;
747
748 DBG("telephony-maemo: list current calls request");
749
750 for (l = calls, i = 1; l != NULL; l = l->next, i++) {
751 struct csd_call *call = l->data;
752 int status, direction, multiparty;
753
754 status = csd_status_to_hfp(call);
755 if (status < 0)
756 continue;
757
758 direction = call->originating ?
759 CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
760
761 multiparty = call->conference ?
762 CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
763
764 telephony_list_current_call_ind(i, direction, status,
765 CALL_MODE_VOICE, multiparty,
766 call->number,
767 number_type(call->number));
768 }
769
770 telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
771}
772
773void telephony_operator_selection_req(void *telephony_device)
774{
775 telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
776 net.operator_name ? net.operator_name : "");
777 telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
778}
779
780static void foreach_call_with_status(int status,
781 int (*func)(struct csd_call *call))
782{
783 GSList *l;
784
785 for (l = calls; l != NULL; l = l->next) {
786 struct csd_call *call = l->data;
787
788 if (call->status == status)
789 func(call);
790 }
791}
792
793void telephony_call_hold_req(void *telephony_device, const char *cmd)
794{
795 const char *idx;
796 struct csd_call *call;
797 int err = 0;
798
799 DBG("telephony-maemo: got call hold request %s", cmd);
800
801 if (strlen(cmd) > 1)
802 idx = &cmd[1];
803 else
804 idx = NULL;
805
806 if (idx)
807 call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
808 else
809 call = NULL;
810
811 switch (cmd[0]) {
812 case '0':
813 foreach_call_with_status(CSD_CALL_STATUS_HOLD, release_call);
814 foreach_call_with_status(CSD_CALL_STATUS_WAITING,
815 release_call);
816 break;
817 case '1':
818 if (idx) {
819 if (call)
820 err = release_call(call);
821 break;
822 }
823 foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
824 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
825 if (call)
826 err = answer_call(call);
827 break;
828 case '2':
829 if (idx) {
830 if (call)
831 err = split_call(call);
832 } else {
833 struct csd_call *held, *wait;
834
835 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
836 held = find_call_with_status(CSD_CALL_STATUS_HOLD);
837 wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
838
839 if (wait)
840 err = answer_call(wait);
841 else if (call && held)
842 err = swap_calls();
843 else {
844 if (call)
845 err = hold_call(call);
846 if (held)
847 err = unhold_call(held);
848 }
849 }
850 break;
851 case '3':
852 if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
853 find_call_with_status(CSD_CALL_STATUS_WAITING))
854 err = create_conference();
855 break;
856 case '4':
857 err = call_transfer();
858 break;
859 default:
860 DBG("Unknown call hold request");
861 break;
862 }
863
864 if (err)
865 telephony_call_hold_rsp(telephony_device,
866 CME_ERROR_AG_FAILURE);
867 else
868 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
869}
870
871void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
872{
873 DBG("telephony-maemo: got %s NR and EC request",
874 enable ? "enable" : "disable");
875 telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
876}
877
878void telephony_key_press_req(void *telephony_device, const char *keys)
879{
880 struct csd_call *active, *waiting;
881 int err;
882
883 DBG("telephony-maemo: got key press request for %s", keys);
884
885 waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
886 if (!waiting)
887 waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
888 if (!waiting)
889 waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
890
891 active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
892
893 if (waiting)
894 err = answer_call(waiting);
895 else if (active)
896 err = release_call(active);
897 else
898 err = 0;
899
900 if (err < 0)
901 telephony_key_press_rsp(telephony_device,
902 CME_ERROR_AG_FAILURE);
903 else
904 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
905}
906
907void telephony_voice_dial_req(void *telephony_device, gboolean enable)
908{
909 DBG("telephony-maemo: got %s voice dial request",
910 enable ? "enable" : "disable");
911
912 telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
913}
914
915static void handle_incoming_call(DBusMessage *msg)
916{
917 const char *number, *call_path;
918 struct csd_call *call;
919
920 if (!dbus_message_get_args(msg, NULL,
921 DBUS_TYPE_OBJECT_PATH, &call_path,
922 DBUS_TYPE_STRING, &number,
923 DBUS_TYPE_INVALID)) {
924 error("Unexpected parameters in Call.Coming() signal");
925 return;
926 }
927
928 call = find_call(call_path);
929 if (!call) {
930 error("Didn't find any matching call object for %s",
931 call_path);
932 return;
933 }
934
935 DBG("Incoming call to %s from number %s", call_path, number);
936
937 g_free(call->number);
938 call->number = g_strdup(number);
939
940 telephony_update_indicator(maemo_indicators, "callsetup",
941 EV_CALLSETUP_INCOMING);
942
943 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
944 telephony_call_waiting_ind(call->number,
945 number_type(call->number));
946 else
947 telephony_incoming_call_ind(call->number,
948 number_type(call->number));
949}
950
951static void handle_outgoing_call(DBusMessage *msg)
952{
953 const char *number, *call_path;
954 struct csd_call *call;
955
956 if (!dbus_message_get_args(msg, NULL,
957 DBUS_TYPE_OBJECT_PATH, &call_path,
958 DBUS_TYPE_STRING, &number,
959 DBUS_TYPE_INVALID)) {
960 error("Unexpected parameters in Call.Created() signal");
961 return;
962 }
963
964 call = find_call(call_path);
965 if (!call) {
966 error("Didn't find any matching call object for %s",
967 call_path);
968 return;
969 }
970
971 DBG("Outgoing call from %s to number %s", call_path, number);
972
973 g_free(call->number);
974 call->number = g_strdup(number);
975
976 g_free(last_dialed_number);
977 last_dialed_number = g_strdup(number);
978
979 if (create_request_timer) {
980 g_source_remove(create_request_timer);
981 create_request_timer = 0;
982 }
983}
984
985static gboolean create_timeout(gpointer user_data)
986{
987 telephony_update_indicator(maemo_indicators, "callsetup",
988 EV_CALLSETUP_INACTIVE);
989 create_request_timer = 0;
990 return FALSE;
991}
992
993static void handle_create_requested(DBusMessage *msg)
994{
995 DBG("Call.CreateRequested()");
996
997 if (create_request_timer)
998 g_source_remove(create_request_timer);
999
1000 create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
1001
1002 telephony_update_indicator(maemo_indicators, "callsetup",
1003 EV_CALLSETUP_OUTGOING);
1004}
1005
1006static void handle_call_status(DBusMessage *msg, const char *call_path)
1007{
1008 struct csd_call *call;
1009 dbus_uint32_t status, cause_type, cause;
1010 int callheld = telephony_get_indicator(maemo_indicators, "callheld");
1011
1012 if (!dbus_message_get_args(msg, NULL,
1013 DBUS_TYPE_UINT32, &status,
1014 DBUS_TYPE_UINT32, &cause_type,
1015 DBUS_TYPE_UINT32, &cause,
1016 DBUS_TYPE_INVALID)) {
1017 error("Unexpected paramters in Instance.CallStatus() signal");
1018 return;
1019 }
1020
1021 call = find_call(call_path);
1022 if (!call) {
1023 error("Didn't find any matching call object for %s",
1024 call_path);
1025 return;
1026 }
1027
1028 if (status > 16) {
1029 error("Invalid call status %u", status);
1030 return;
1031 }
1032
1033 DBG("Call %s changed from %s to %s", call_path,
1034 call_status_str[call->status], call_status_str[status]);
1035
1036 if (call->status == (int) status) {
1037 DBG("Ignoring CSD Call state change to existing state");
1038 return;
1039 }
1040
1041 call->status = (int) status;
1042
1043 switch (status) {
1044 case CSD_CALL_STATUS_IDLE:
1045 if (call->setup) {
1046 telephony_update_indicator(maemo_indicators,
1047 "callsetup",
1048 EV_CALLSETUP_INACTIVE);
1049 if (!call->originating)
1050 telephony_calling_stopped_ind();
1051 }
1052
1053 g_free(call->number);
1054 call->number = NULL;
1055 call->originating = FALSE;
1056 call->emergency = FALSE;
1057 call->on_hold = FALSE;
1058 call->conference = FALSE;
1059 call->setup = FALSE;
1060 break;
1061 case CSD_CALL_STATUS_CREATE:
1062 call->originating = TRUE;
1063 call->setup = TRUE;
1064 break;
1065 case CSD_CALL_STATUS_COMING:
1066 call->originating = FALSE;
1067 call->setup = TRUE;
1068 break;
1069 case CSD_CALL_STATUS_PROCEEDING:
1070 break;
1071 case CSD_CALL_STATUS_MO_ALERTING:
1072 telephony_update_indicator(maemo_indicators, "callsetup",
1073 EV_CALLSETUP_ALERTING);
1074 break;
1075 case CSD_CALL_STATUS_MT_ALERTING:
1076 break;
1077 case CSD_CALL_STATUS_WAITING:
1078 break;
1079 case CSD_CALL_STATUS_ANSWERED:
1080 break;
1081 case CSD_CALL_STATUS_ACTIVE:
1082 if (call->on_hold) {
1083 call->on_hold = FALSE;
1084 if (find_call_with_status(CSD_CALL_STATUS_HOLD))
1085 telephony_update_indicator(maemo_indicators,
1086 "callheld",
1087 EV_CALLHELD_MULTIPLE);
1088 else
1089 telephony_update_indicator(maemo_indicators,
1090 "callheld",
1091 EV_CALLHELD_NONE);
1092 } else {
1093 if (!g_slist_find(active_calls, call))
1094 active_calls = g_slist_prepend(active_calls, call);
1095 if (g_slist_length(active_calls) == 1)
1096 telephony_update_indicator(maemo_indicators,
1097 "call",
1098 EV_CALL_ACTIVE);
1099 /* Upgrade callheld status if necessary */
1100 if (callheld == EV_CALLHELD_ON_HOLD)
1101 telephony_update_indicator(maemo_indicators,
1102 "callheld",
1103 EV_CALLHELD_MULTIPLE);
1104 telephony_update_indicator(maemo_indicators,
1105 "callsetup",
1106 EV_CALLSETUP_INACTIVE);
1107 if (!call->originating)
1108 telephony_calling_stopped_ind();
1109 call->setup = FALSE;
1110 }
1111 break;
1112 case CSD_CALL_STATUS_MO_RELEASE:
1113 case CSD_CALL_STATUS_MT_RELEASE:
1114 active_calls = g_slist_remove(active_calls, call);
1115 if (g_slist_length(active_calls) == 0)
1116 telephony_update_indicator(maemo_indicators, "call",
1117 EV_CALL_INACTIVE);
1118 break;
1119 case CSD_CALL_STATUS_HOLD_INITIATED:
1120 break;
1121 case CSD_CALL_STATUS_HOLD:
1122 call->on_hold = TRUE;
1123 if (find_non_held_call())
1124 telephony_update_indicator(maemo_indicators,
1125 "callheld",
1126 EV_CALLHELD_MULTIPLE);
1127 else
1128 telephony_update_indicator(maemo_indicators,
1129 "callheld",
1130 EV_CALLHELD_ON_HOLD);
1131 break;
1132 case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1133 break;
1134 case CSD_CALL_STATUS_RECONNECT_PENDING:
1135 break;
1136 case CSD_CALL_STATUS_TERMINATED:
1137 if (call->on_hold &&
1138 !find_call_with_status(CSD_CALL_STATUS_HOLD))
1139 telephony_update_indicator(maemo_indicators,
1140 "callheld",
1141 EV_CALLHELD_NONE);
1142 else if (callheld == EV_CALLHELD_MULTIPLE &&
1143 find_call_with_status(CSD_CALL_STATUS_HOLD))
1144 telephony_update_indicator(maemo_indicators,
1145 "callheld",
1146 EV_CALLHELD_ON_HOLD);
1147 break;
1148 case CSD_CALL_STATUS_SWAP_INITIATED:
1149 break;
1150 default:
1151 error("Unknown call status %u", status);
1152 break;
1153 }
1154}
1155
1156static void handle_conference(DBusMessage *msg, gboolean joined)
1157{
1158 const char *path;
1159 struct csd_call *call;
1160
1161 if (!dbus_message_get_args(msg, NULL,
1162 DBUS_TYPE_OBJECT_PATH, &path,
1163 DBUS_TYPE_INVALID)) {
1164 error("Unexpected parameters in Conference.%s",
1165 dbus_message_get_member(msg));
1166 return;
1167 }
1168
1169 call = find_call(path);
1170 if (!call) {
1171 error("Conference signal for unknown call %s", path);
1172 return;
1173 }
1174
1175 DBG("Call %s %s the conference", path, joined ? "joined" : "left");
1176
1177 call->conference = joined;
1178}
1179
1180static void get_operator_name_reply(DBusPendingCall *pending_call,
1181 void *user_data)
1182{
1183 DBusMessage *reply;
1184 DBusError err;
1185 const char *name;
1186 dbus_int32_t net_err;
1187
1188 reply = dbus_pending_call_steal_reply(pending_call);
1189
1190 dbus_error_init(&err);
1191 if (dbus_set_error_from_message(&err, reply)) {
1192 error("get_operator_name failed: %s, %s",
1193 err.name, err.message);
1194 dbus_error_free(&err);
1195 goto done;
1196 }
1197
1198 dbus_error_init(&err);
1199 if (!dbus_message_get_args(reply, &err,
1200 DBUS_TYPE_STRING, &name,
1201 DBUS_TYPE_INT32, &net_err,
1202 DBUS_TYPE_INVALID)) {
1203 error("Unexpected get_operator_name reply parameters: %s, %s",
1204 err.name, err.message);
1205 dbus_error_free(&err);
1206 goto done;
1207 }
1208
1209 if (net_err != 0) {
1210 error("get_operator_name failed with code %d", net_err);
1211 goto done;
1212 }
1213
1214 if (strlen(name) == 0)
1215 goto done;
1216
1217 g_free(net.operator_name);
1218 net.operator_name = g_strdup(name);
1219
1220 DBG("telephony-maemo: operator name updated: %s", name);
1221
1222done:
1223 dbus_message_unref(reply);
1224}
1225
1226static void resolve_operator_name(uint32_t operator, uint32_t country)
1227{
1228 uint8_t name_type = NETWORK_HARDCODED_LATIN_OPER_NAME;
1229
1230 send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
1231 NETWORK_INTERFACE, "get_operator_name",
1232 get_operator_name_reply, NULL,
1233 DBUS_TYPE_BYTE, &name_type,
1234 DBUS_TYPE_UINT32, &operator,
1235 DBUS_TYPE_UINT32, &country,
1236 DBUS_TYPE_INVALID);
1237}
1238
1239static void update_registration_status(uint8_t status, uint16_t lac,
1240 uint32_t cell_id,
1241 uint32_t operator_code,
1242 uint32_t country_code,
1243 uint8_t network_type,
1244 uint8_t supported_services)
1245{
1246 if (net.status != status) {
1247 switch (status) {
1248 case NETWORK_REG_STATUS_HOME:
1249 telephony_update_indicator(maemo_indicators, "roam",
1250 EV_ROAM_INACTIVE);
1251 if (net.status >= NETWORK_REG_STATUS_NOSERV)
1252 telephony_update_indicator(maemo_indicators,
1253 "service",
1254 EV_SERVICE_PRESENT);
1255 break;
1256 case NETWORK_REG_STATUS_ROAM:
1257 case NETWORK_REG_STATUS_ROAM_BLINK:
1258 telephony_update_indicator(maemo_indicators, "roam",
1259 EV_ROAM_ACTIVE);
1260 if (net.status >= NETWORK_REG_STATUS_NOSERV)
1261 telephony_update_indicator(maemo_indicators,
1262 "service",
1263 EV_SERVICE_PRESENT);
1264 break;
1265 case NETWORK_REG_STATUS_NOSERV:
1266 case NETWORK_REG_STATUS_NOSERV_SEARCHING:
1267 case NETWORK_REG_STATUS_NOSERV_NOTSEARCHING:
1268 case NETWORK_REG_STATUS_NOSERV_NOSIM:
1269 case NETWORK_REG_STATUS_POWER_OFF:
1270 case NETWORK_REG_STATUS_NSPS:
1271 case NETWORK_REG_STATUS_NSPS_NO_COVERAGE:
1272 case NETWORK_REG_STATUS_NOSERV_SIM_REJECTED_BY_NW:
1273 if (net.status < NETWORK_REG_STATUS_NOSERV)
1274 telephony_update_indicator(maemo_indicators,
1275 "service",
1276 EV_SERVICE_NONE);
1277 break;
1278 }
1279
1280 net.status = status;
1281 }
1282
1283 net.lac = lac;
1284 net.cell_id = cell_id;
1285
1286 if (net.operator_code != operator_code ||
1287 net.country_code != country_code) {
1288 g_free(net.operator_name);
1289 net.operator_name = NULL;
1290 resolve_operator_name(operator_code, country_code);
1291 net.operator_code = operator_code;
1292 net.country_code = country_code;
1293 }
1294
1295 net.network_type = network_type;
1296 net.supported_services = supported_services;
1297}
1298
1299static void handle_registration_status_change(DBusMessage *msg)
1300{
1301 uint8_t status;
1302 dbus_uint16_t lac, network_type, supported_services;
1303 dbus_uint32_t cell_id, operator_code, country_code;
1304
1305 if (!dbus_message_get_args(msg, NULL,
1306 DBUS_TYPE_BYTE, &status,
1307 DBUS_TYPE_UINT16, &lac,
1308 DBUS_TYPE_UINT32, &cell_id,
1309 DBUS_TYPE_UINT32, &operator_code,
1310 DBUS_TYPE_UINT32, &country_code,
1311 DBUS_TYPE_BYTE, &network_type,
1312 DBUS_TYPE_BYTE, &supported_services,
1313 DBUS_TYPE_INVALID)) {
1314 error("Unexpected parameters in registration_status_change");
1315 return;
1316 }
1317
1318 update_registration_status(status, lac, cell_id, operator_code,
1319 country_code, network_type,
1320 supported_services);
1321}
1322
1323static void update_signal_strength(uint8_t signals_bar)
1324{
1325 int signal;
1326
1327 if (signals_bar > 100) {
1328 DBG("signals_bar greater than expected: %u", signals_bar);
1329 signals_bar = 100;
1330 }
1331
1332 if (net.signals_bar == signals_bar)
1333 return;
1334
1335 /* A simple conversion from 0-100 to 0-5 (used by HFP) */
1336 signal = (signals_bar + 20) / 21;
1337
1338 telephony_update_indicator(maemo_indicators, "signal", signal);
1339
1340 net.signals_bar = signals_bar;
1341
1342 DBG("Signal strength updated: %u/100, %d/5", signals_bar, signal);
1343}
1344
1345static void handle_signal_strength_change(DBusMessage *msg)
1346{
1347 uint8_t signals_bar, rssi_in_dbm;
1348
1349 if (!dbus_message_get_args(msg, NULL,
1350 DBUS_TYPE_BYTE, &signals_bar,
1351 DBUS_TYPE_BYTE, &rssi_in_dbm,
1352 DBUS_TYPE_INVALID)) {
1353 error("Unexpected parameters in signal_strength_change");
1354 return;
1355 }
1356
1357 update_signal_strength(signals_bar);
1358}
1359
1360static gboolean iter_get_basic_args(DBusMessageIter *iter,
1361 int first_arg_type, ...)
1362{
1363 int type;
1364 va_list ap;
1365
1366 va_start(ap, first_arg_type);
1367
1368 for (type = first_arg_type; type != DBUS_TYPE_INVALID;
1369 type = va_arg(ap, int)) {
1370 void *value = va_arg(ap, void *);
1371 int real_type = dbus_message_iter_get_arg_type(iter);
1372
1373 if (real_type != type) {
1374 error("iter_get_basic_args: expected %c but got %c",
1375 (char) type, (char) real_type);
1376 break;
1377 }
1378
1379 dbus_message_iter_get_basic(iter, value);
1380 dbus_message_iter_next(iter);
1381 }
1382
1383 va_end(ap);
1384
1385 return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
1386}
1387
1388static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
1389{
1390 DBusError err;
1391 DBusMessage *reply;
1392 dbus_int32_t level;
1393 int *value = user_data;
1394
1395 reply = dbus_pending_call_steal_reply(call);
1396
1397 dbus_error_init(&err);
1398 if (dbus_set_error_from_message(&err, reply)) {
1399 error("hald replied with an error: %s, %s",
1400 err.name, err.message);
1401 dbus_error_free(&err);
1402 goto done;
1403 }
1404
1405 dbus_error_init(&err);
1406 if (dbus_message_get_args(reply, &err,
1407 DBUS_TYPE_INT32, &level,
1408 DBUS_TYPE_INVALID) == FALSE) {
1409 error("Unable to parse GetPropertyInteger reply: %s, %s",
1410 err.name, err.message);
1411 dbus_error_free(&err);
1412 goto done;
1413 }
1414
1415 *value = (int) level;
1416
1417 if (value == &battchg_last)
1418 DBG("telephony-maemo: battery.charge_level.last_full is %d",
1419 *value);
1420 else if (value == &battchg_design)
1421 DBG("telephony-maemo: battery.charge_level.design is %d",
1422 *value);
1423 else
1424 DBG("telephony-maemo: battery.charge_level.current is %d",
1425 *value);
1426
1427 if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
1428 int new, max;
1429
1430 if (battchg_last > 0)
1431 max = battchg_last;
1432 else
1433 max = battchg_design;
1434
1435 new = battchg_cur * 5 / max;
1436
1437 telephony_update_indicator(maemo_indicators, "battchg", new);
1438 }
1439done:
1440 dbus_message_unref(reply);
1441}
1442
1443static void hal_get_integer(const char *path, const char *key, void *user_data)
1444{
1445 send_method_call("org.freedesktop.Hal", path,
1446 "org.freedesktop.Hal.Device",
1447 "GetPropertyInteger",
1448 hal_battery_level_reply, user_data,
1449 DBUS_TYPE_STRING, &key,
1450 DBUS_TYPE_INVALID);
1451}
1452
1453static void handle_hal_property_modified(DBusMessage *msg)
1454{
1455 DBusMessageIter iter, array;
1456 dbus_int32_t num_changes;
1457 const char *path;
1458
1459 path = dbus_message_get_path(msg);
1460
1461 dbus_message_iter_init(msg, &iter);
1462
1463 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
1464 error("Unexpected signature in hal PropertyModified signal");
1465 return;
1466 }
1467
1468 dbus_message_iter_get_basic(&iter, &num_changes);
1469 dbus_message_iter_next(&iter);
1470
1471 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1472 error("Unexpected signature in hal PropertyModified signal");
1473 return;
1474 }
1475
1476 dbus_message_iter_recurse(&iter, &array);
1477
1478 while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1479 DBusMessageIter prop;
1480 const char *name;
1481 dbus_bool_t added, removed;
1482
1483 dbus_message_iter_recurse(&array, &prop);
1484
1485 if (!iter_get_basic_args(&prop,
1486 DBUS_TYPE_STRING, &name,
1487 DBUS_TYPE_BOOLEAN, &added,
1488 DBUS_TYPE_BOOLEAN, &removed,
1489 DBUS_TYPE_INVALID)) {
1490 error("Invalid hal PropertyModified parameters");
1491 break;
1492 }
1493
1494 if (g_str_equal(name, "battery.charge_level.last_full"))
1495 hal_get_integer(path, name, &battchg_last);
1496 else if (g_str_equal(name, "battery.charge_level.current"))
1497 hal_get_integer(path, name, &battchg_cur);
1498 else if (g_str_equal(name, "battery.charge_level.design"))
1499 hal_get_integer(path, name, &battchg_design);
1500
1501 dbus_message_iter_next(&array);
1502 }
1503}
1504
1505static void csd_call_free(struct csd_call *call)
1506{
1507 if (!call)
1508 return;
1509
1510 g_free(call->object_path);
1511 g_free(call->number);
1512
1513 g_free(call);
1514}
1515
1516static void parse_call_list(DBusMessageIter *iter)
1517{
1518 do {
1519 DBusMessageIter call_iter;
1520 struct csd_call *call;
1521 const char *object_path, *number;
1522 dbus_uint32_t status;
1523 dbus_bool_t originating, terminating, emerg, on_hold, conf;
1524
1525 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
1526 error("Unexpected signature in GetCallInfoAll reply");
1527 break;
1528 }
1529
1530 dbus_message_iter_recurse(iter, &call_iter);
1531
1532 if (!iter_get_basic_args(&call_iter,
1533 DBUS_TYPE_OBJECT_PATH, &object_path,
1534 DBUS_TYPE_UINT32, &status,
1535 DBUS_TYPE_BOOLEAN, &originating,
1536 DBUS_TYPE_BOOLEAN, &terminating,
1537 DBUS_TYPE_BOOLEAN, &emerg,
1538 DBUS_TYPE_BOOLEAN, &on_hold,
1539 DBUS_TYPE_BOOLEAN, &conf,
1540 DBUS_TYPE_STRING, &number,
1541 DBUS_TYPE_INVALID)) {
1542 error("Parsing call D-Bus parameters failed");
1543 break;
1544 }
1545
1546 call = find_call(object_path);
1547 if (!call) {
1548 call = g_new0(struct csd_call, 1);
1549 call->object_path = g_strdup(object_path);
1550 call->status = (int) status;
1551 calls = g_slist_append(calls, call);
1552 DBG("telephony-maemo: new csd call instance at %s",
1553 object_path);
1554 }
1555
1556 if (call->status == CSD_CALL_STATUS_IDLE)
1557 continue;
1558
1559 /* CSD gives incorrect call_hold property sometimes */
1560 if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
1561 (call->status == CSD_CALL_STATUS_HOLD &&
1562 !on_hold)) {
1563 error("Conflicting call status and on_hold property!");
1564 on_hold = call->status == CSD_CALL_STATUS_HOLD;
1565 }
1566
1567 call->originating = originating;
1568 call->on_hold = on_hold;
1569 call->conference = conf;
1570 g_free(call->number);
1571 call->number = g_strdup(number);
1572
1573 } while (dbus_message_iter_next(iter));
1574}
1575
1576static void signal_strength_reply(DBusPendingCall *call, void *user_data)
1577{
1578 DBusError err;
1579 DBusMessage *reply;
1580 uint8_t signals_bar, rssi_in_dbm;
1581 dbus_int32_t net_err;
1582
1583 reply = dbus_pending_call_steal_reply(call);
1584
1585 dbus_error_init(&err);
1586 if (dbus_set_error_from_message(&err, reply)) {
1587 error("Unable to get signal strength: %s, %s",
1588 err.name, err.message);
1589 dbus_error_free(&err);
1590 goto done;
1591 }
1592
1593 dbus_error_init(&err);
1594 if (!dbus_message_get_args(reply, &err,
1595 DBUS_TYPE_BYTE, &signals_bar,
1596 DBUS_TYPE_BYTE, &rssi_in_dbm,
1597 DBUS_TYPE_INT32, &net_err,
1598 DBUS_TYPE_INVALID)) {
1599 error("Unable to parse signal_strength reply: %s, %s",
1600 err.name, err.message);
1601 dbus_error_free(&err);
1602 goto done;
1603 }
1604
1605 if (net_err != 0) {
1606 error("get_signal_strength failed with code %d", net_err);
1607 goto done;
1608 }
1609
1610 update_signal_strength(signals_bar);
1611
1612done:
1613 dbus_message_unref(reply);
1614}
1615
1616static int get_signal_strength(void)
1617{
1618 return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
1619 NETWORK_INTERFACE, "get_signal_strength",
1620 signal_strength_reply, NULL,
1621 DBUS_TYPE_INVALID);
1622}
1623
1624static void registration_status_reply(DBusPendingCall *call, void *user_data)
1625{
1626 DBusError err;
1627 DBusMessage *reply;
1628 uint8_t status;
1629 dbus_uint16_t lac, network_type, supported_services;
1630 dbus_uint32_t cell_id, operator_code, country_code;
1631 dbus_int32_t net_err;
1632
1633 reply = dbus_pending_call_steal_reply(call);
1634
1635 dbus_error_init(&err);
1636 if (dbus_set_error_from_message(&err, reply)) {
1637 error("Unable to get registration status: %s, %s",
1638 err.name, err.message);
1639 dbus_error_free(&err);
1640 goto done;
1641 }
1642
1643 dbus_error_init(&err);
1644 if (!dbus_message_get_args(reply, &err,
1645 DBUS_TYPE_BYTE, &status,
1646 DBUS_TYPE_UINT16, &lac,
1647 DBUS_TYPE_UINT32, &cell_id,
1648 DBUS_TYPE_UINT32, &operator_code,
1649 DBUS_TYPE_UINT32, &country_code,
1650 DBUS_TYPE_BYTE, &network_type,
1651 DBUS_TYPE_BYTE, &supported_services,
1652 DBUS_TYPE_INT32, &net_err,
1653 DBUS_TYPE_INVALID)) {
1654 error("Unable to parse registration_status_change reply:"
1655 " %s, %s", err.name, err.message);
1656 dbus_error_free(&err);
1657 goto done;
1658 }
1659
1660 if (net_err != 0) {
1661 error("get_registration_status failed with code %d", net_err);
1662 goto done;
1663 }
1664
1665 update_registration_status(status, lac, cell_id, operator_code,
1666 country_code, network_type,
1667 supported_services);
1668
1669 get_signal_strength();
1670
1671done:
1672 dbus_message_unref(reply);
1673}
1674
1675static int get_registration_status(void)
1676{
1677 return send_method_call(NETWORK_BUS_NAME, NETWORK_PATH,
1678 NETWORK_INTERFACE, "get_registration_status",
1679 registration_status_reply, NULL,
1680 DBUS_TYPE_INVALID);
1681}
1682
1683static void call_info_reply(DBusPendingCall *call, void *user_data)
1684{
1685 DBusError err;
1686 DBusMessage *reply;
1687 DBusMessageIter iter, sub;
1688
1689 get_calls_active = FALSE;
1690
1691 reply = dbus_pending_call_steal_reply(call);
1692
1693 dbus_error_init(&err);
1694 if (dbus_set_error_from_message(&err, reply)) {
1695 error("csd replied with an error: %s, %s",
1696 err.name, err.message);
1697 dbus_error_free(&err);
1698 goto done;
1699 }
1700
1701 dbus_message_iter_init(reply, &iter);
1702
1703 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1704 error("Unexpected signature in GetCallInfoAll return");
1705 goto done;
1706 }
1707
1708 dbus_message_iter_recurse(&iter, &sub);
1709
1710 parse_call_list(&sub);
1711
1712 get_registration_status();
1713
1714done:
1715 dbus_message_unref(reply);
1716}
1717
1718static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
1719{
1720 DBusError err;
1721 DBusMessage *reply;
1722 DBusMessageIter iter, sub;
1723 const char *path;
1724 char match_string[256];
1725 int type;
1726
1727 reply = dbus_pending_call_steal_reply(call);
1728
1729 dbus_error_init(&err);
1730 if (dbus_set_error_from_message(&err, reply)) {
1731 error("hald replied with an error: %s, %s",
1732 err.name, err.message);
1733 dbus_error_free(&err);
1734 goto done;
1735 }
1736
1737 dbus_message_iter_init(reply, &iter);
1738
1739 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1740 error("Unexpected signature in FindDeviceByCapability return");
1741 goto done;
1742 }
1743
1744 dbus_message_iter_recurse(&iter, &sub);
1745
1746 type = dbus_message_iter_get_arg_type(&sub);
1747
1748 if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
1749 error("No hal device with battery capability found");
1750 goto done;
1751 }
1752
1753 dbus_message_iter_get_basic(&sub, &path);
1754
1755 DBG("telephony-maemo: found battery device at %s", path);
1756
1757 snprintf(match_string, sizeof(match_string),
1758 "type='signal',"
1759 "path='%s',"
1760 "interface='org.freedesktop.Hal.Device',"
1761 "member='PropertyModified'", path);
1762 dbus_bus_add_match(connection, match_string, NULL);
1763
1764 hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
1765 hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
1766 hal_get_integer(path, "battery.charge_level.design", &battchg_design);
1767
1768done:
1769 dbus_message_unref(reply);
1770}
1771
1772static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
1773{
1774 DBusError derr;
1775 DBusMessage *reply;
1776 const char *name, *number;
1777 char **number_type = user_data;
1778 dbus_int32_t current_location, err;
1779
1780 reply = dbus_pending_call_steal_reply(call);
1781
1782 dbus_error_init(&derr);
1783 if (dbus_set_error_from_message(&derr, reply)) {
1784 error("SIM.Phonebook replied with an error: %s, %s",
1785 derr.name, derr.message);
1786 dbus_error_free(&derr);
1787 goto done;
1788 }
1789
1790 dbus_error_init(&derr);
1791 if (dbus_message_get_args(reply, &derr,
1792 DBUS_TYPE_STRING, &name,
1793 DBUS_TYPE_STRING, &number,
1794 DBUS_TYPE_INT32, &current_location,
1795 DBUS_TYPE_INT32, &err,
1796 DBUS_TYPE_INVALID) == FALSE) {
1797 error("Unable to parse SIM.Phonebook.read arguments: %s, %s",
1798 derr.name, derr.message);
1799 dbus_error_free(&derr);
1800 goto done;
1801 }
1802
1803 if (err != 0) {
1804 error("SIM.Phonebook.read failed with error %d", err);
1805 if (number_type == &vmbx)
1806 vmbx = g_strdup(getenv("VMBX_NUMBER"));
1807 goto done;
1808 }
1809
1810 if (number_type == &msisdn) {
1811 g_free(msisdn);
1812 msisdn = g_strdup(number);
1813 DBG("Got MSISDN %s (%s)", number, name);
1814 } else {
1815 g_free(vmbx);
1816 vmbx = g_strdup(number);
1817 DBG("Got voice mailbox number %s (%s)", number, name);
1818 }
1819
1820done:
1821 dbus_message_unref(reply);
1822}
1823
1824static void csd_init(void)
1825{
1826 dbus_uint32_t location;
1827 uint8_t pb_type, location_type;
1828 int ret;
1829
1830 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1831 CSD_CALL_INTERFACE, "GetCallInfoAll",
1832 call_info_reply, NULL, DBUS_TYPE_INVALID);
1833 if (ret < 0) {
1834 error("Unable to sent GetCallInfoAll method call");
1835 return;
1836 }
1837
1838 get_calls_active = TRUE;
1839
1840 pb_type = SIM_PHONEBOOK_TYPE_MSISDN;
1841 location = PHONEBOOK_INDEX_FIRST_ENTRY;
1842 location_type = SIM_PHONEBOOK_LOCATION_NEXT;
1843
1844 ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
1845 SIM_PHONEBOOK_INTERFACE, "read",
1846 phonebook_read_reply, &msisdn,
1847 DBUS_TYPE_BYTE, &pb_type,
1848 DBUS_TYPE_INT32, &location,
1849 DBUS_TYPE_BYTE, &location_type,
1850 DBUS_TYPE_INVALID);
1851 if (ret < 0) {
1852 error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
1853 return;
1854 }
1855
1856 pb_type = SIM_PHONEBOOK_TYPE_MBDN;
1857 location = PHONEBOOK_INDEX_FIRST_ENTRY;
1858 location_type = SIM_PHONEBOOK_LOCATION_NEXT;
1859
1860 ret = send_method_call(SIM_PHONEBOOK_BUS_NAME, SIM_PHONEBOOK_PATH,
1861 SIM_PHONEBOOK_INTERFACE, "read",
1862 phonebook_read_reply, &vmbx,
1863 DBUS_TYPE_BYTE, &pb_type,
1864 DBUS_TYPE_INT32, &location,
1865 DBUS_TYPE_BYTE, &location_type,
1866 DBUS_TYPE_INVALID);
1867 if (ret < 0) {
1868 error("Unable to send " SIM_PHONEBOOK_INTERFACE ".read()");
1869 return;
1870 }
1871}
1872
1873static uint32_t get_callflag(const char *callerid_setting)
1874{
1875 if (callerid_setting != NULL) {
1876 if (g_str_equal(callerid_setting, "allowed"))
1877 return CALL_FLAG_PRESENTATION_ALLOWED;
1878 else if (g_str_equal(callerid_setting, "restricted"))
1879 return CALL_FLAG_PRESENTATION_RESTRICTED;
1880 else
1881 return CALL_FLAG_NONE;
1882 } else
1883 return CALL_FLAG_NONE;
1884}
1885
1886static void generate_flag_file(const char *filename)
1887{
1888 int fd;
1889
1890 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
1891 g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS) ||
1892 g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1893 return;
1894
1895 fd = open(filename, O_WRONLY | O_CREAT, 0);
1896 if (fd >= 0)
1897 close(fd);
1898}
1899
1900static void save_callerid_to_file(const char *callerid_setting)
1901{
1902 char callerid_file[FILENAME_MAX];
1903
1904 snprintf(callerid_file, sizeof(callerid_file), "%s%s",
1905 CALLERID_BASE, callerid_setting);
1906
1907 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
1908 rename(ALLOWED_FLAG_FILE, callerid_file);
1909 else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
1910 rename(RESTRICTED_FLAG_FILE, callerid_file);
1911 else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1912 rename(NONE_FLAG_FILE, callerid_file);
1913 else
1914 generate_flag_file(callerid_file);
1915}
1916
1917static uint32_t callerid_from_file(void)
1918{
1919 if (g_file_test(ALLOWED_FLAG_FILE, G_FILE_TEST_EXISTS))
1920 return CALL_FLAG_PRESENTATION_ALLOWED;
1921 else if (g_file_test(RESTRICTED_FLAG_FILE, G_FILE_TEST_EXISTS))
1922 return CALL_FLAG_PRESENTATION_RESTRICTED;
1923 else if (g_file_test(NONE_FLAG_FILE, G_FILE_TEST_EXISTS))
1924 return CALL_FLAG_NONE;
1925 else
1926 return CALL_FLAG_NONE;
1927}
1928
1929static DBusMessage *set_callerid(DBusConnection *conn, DBusMessage *msg,
1930 void *data)
1931{
1932 const char *callerid_setting;
1933
1934 if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
1935 &callerid_setting,
1936 DBUS_TYPE_INVALID) == FALSE)
1937 return btd_error_invalid_args(msg);
1938
1939 if (g_str_equal(callerid_setting, "allowed") ||
1940 g_str_equal(callerid_setting, "restricted") ||
1941 g_str_equal(callerid_setting, "none")) {
1942 save_callerid_to_file(callerid_setting);
1943 callerid = get_callflag(callerid_setting);
1944 DBG("telephony-maemo setting callerid flag: %s",
1945 callerid_setting);
1946 return dbus_message_new_method_return(msg);
1947 }
1948
1949 error("telephony-maemo: invalid argument %s for method call"
1950 " SetCallerId", callerid_setting);
1951 return btd_error_invalid_args(msg);
1952}
1953
1954static GDBusMethodTable telephony_maemo_methods[] = {
1955 {"SetCallerId", "s", "", set_callerid,
1956 G_DBUS_METHOD_FLAG_ASYNC},
1957 { }
1958};
1959
1960static void handle_modem_state(DBusMessage *msg)
1961{
1962 const char *state;
1963
1964 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
1965 DBUS_TYPE_INVALID)) {
1966 error("Unexpected modem state parameters");
1967 return;
1968 }
1969
1970 DBG("SSC modem state: %s", state);
1971
1972 if (calls != NULL || get_calls_active)
1973 return;
1974
1975 if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
1976 csd_init();
1977}
1978
1979static void modem_state_reply(DBusPendingCall *call, void *user_data)
1980{
1981 DBusMessage *reply = dbus_pending_call_steal_reply(call);
1982 DBusError err;
1983
1984 dbus_error_init(&err);
1985 if (dbus_set_error_from_message(&err, reply)) {
1986 error("get_modem_status: %s, %s", err.name, err.message);
1987 dbus_error_free(&err);
1988 } else
1989 handle_modem_state(reply);
1990
1991 dbus_message_unref(reply);
1992}
1993
1994static DBusHandlerResult signal_filter(DBusConnection *conn,
1995 DBusMessage *msg, void *data)
1996{
1997 const char *path = dbus_message_get_path(msg);
1998
1999 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
2000 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2001
2002 if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
2003 handle_incoming_call(msg);
2004 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
2005 handle_outgoing_call(msg);
2006 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
2007 "CreateRequested"))
2008 handle_create_requested(msg);
2009 else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
2010 handle_call_status(msg, path);
2011 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
2012 handle_conference(msg, TRUE);
2013 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
2014 handle_conference(msg, FALSE);
2015 else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
2016 "registration_status_change"))
2017 handle_registration_status_change(msg);
2018 else if (dbus_message_is_signal(msg, NETWORK_INTERFACE,
2019 "signal_strength_change"))
2020 handle_signal_strength_change(msg);
2021 else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
2022 "PropertyModified"))
2023 handle_hal_property_modified(msg);
2024 else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
2025 "modem_state_changed_ind"))
2026 handle_modem_state(msg);
2027
2028 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2029}
2030
2031int telephony_init(void)
2032{
2033 const char *battery_cap = "battery";
2034 uint32_t features = AG_FEATURE_EC_ANDOR_NR |
2035 AG_FEATURE_INBAND_RINGTONE |
2036 AG_FEATURE_REJECT_A_CALL |
2037 AG_FEATURE_ENHANCED_CALL_STATUS |
2038 AG_FEATURE_ENHANCED_CALL_CONTROL |
2039 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
2040 AG_FEATURE_THREE_WAY_CALLING;
2041
2042 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
2043
2044 if (!dbus_connection_add_filter(connection, signal_filter,
2045 NULL, NULL))
2046 error("Can't add signal filter");
2047
2048 dbus_bus_add_match(connection,
2049 "type=signal,interface=" CSD_CALL_INTERFACE, NULL);
2050 dbus_bus_add_match(connection,
2051 "type=signal,interface=" CSD_CALL_INSTANCE, NULL);
2052 dbus_bus_add_match(connection,
2053 "type=signal,interface=" CSD_CALL_CONFERENCE, NULL);
2054 dbus_bus_add_match(connection,
2055 "type=signal,interface=" NETWORK_INTERFACE, NULL);
2056 dbus_bus_add_match(connection,
2057 "type=signal,interface=" SSC_DBUS_IFACE
2058 ",member=modem_state_changed_ind", NULL);
2059
2060 if (send_method_call(SSC_DBUS_NAME, SSC_DBUS_PATH, SSC_DBUS_IFACE,
2061 "get_modem_state", modem_state_reply,
2062 NULL, DBUS_TYPE_INVALID) < 0)
2063 error("Unable to send " SSC_DBUS_IFACE ".get_modem_state()");
2064
2065 generate_flag_file(NONE_FLAG_FILE);
2066 callerid = callerid_from_file();
2067
2068 if (!g_dbus_register_interface(connection, TELEPHONY_MAEMO_PATH,
2069 TELEPHONY_MAEMO_INTERFACE, telephony_maemo_methods,
2070 NULL, NULL, NULL, NULL)) {
2071 error("telephony-maemo interface %s init failed on path %s",
2072 TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
2073 }
2074
2075 DBG("telephony-maemo registering %s interface on path %s",
2076 TELEPHONY_MAEMO_INTERFACE, TELEPHONY_MAEMO_PATH);
2077
2078 telephony_ready_ind(features, maemo_indicators, BTRH_NOT_SUPPORTED,
2079 chld_str);
2080 if (send_method_call("org.freedesktop.Hal",
2081 "/org/freedesktop/Hal/Manager",
2082 "org.freedesktop.Hal.Manager",
2083 "FindDeviceByCapability",
2084 hal_find_device_reply, NULL,
2085 DBUS_TYPE_STRING, &battery_cap,
2086 DBUS_TYPE_INVALID) < 0)
2087 error("Unable to send HAL method call");
2088
2089 return 0;
2090}
2091
2092void telephony_exit(void)
2093{
2094 g_slist_foreach(calls, (GFunc) csd_call_free, NULL);
2095 g_slist_free(calls);
2096 calls = NULL;
2097
2098 dbus_connection_remove_filter(connection, signal_filter, NULL);
2099
2100 dbus_connection_unref(connection);
2101 connection = NULL;
2102
2103 telephony_deinit();
2104}
02105
=== added file '.pc/02_disable_hal.patch/audio/telephony-maemo6.c'
--- .pc/02_disable_hal.patch/audio/telephony-maemo6.c 1970-01-01 00:00:00 +0000
+++ .pc/02_disable_hal.patch/audio/telephony-maemo6.c 2013-04-19 02:35:36 +0000
@@ -0,0 +1,2201 @@
1/*
2 *
3 * BlueZ - Bluetooth protocol stack for Linux
4 *
5 * Copyright (C) 2008-2010 Nokia Corporation
6 * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
7 *
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 *
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#endif
28
29#include <stdlib.h>
30#include <stdio.h>
31#include <unistd.h>
32#include <fcntl.h>
33#include <stdint.h>
34#include <string.h>
35#include <glib.h>
36#include <dbus/dbus.h>
37#include <gdbus.h>
38
39#include <bluetooth/sdp.h>
40
41#include "glib-compat.h"
42#include "log.h"
43#include "telephony.h"
44#include "error.h"
45
46/* SSC D-Bus definitions */
47#define SSC_DBUS_NAME "com.nokia.phone.SSC"
48#define SSC_DBUS_IFACE "com.nokia.phone.SSC"
49#define SSC_DBUS_PATH "/com/nokia/phone/SSC"
50
51/* libcsnet D-Bus definitions */
52#define CSD_CSNET_BUS_NAME "com.nokia.csd.CSNet"
53#define CSD_CSNET_PATH "/com/nokia/csd/csnet"
54#define CSD_CSNET_IFACE "com.nokia.csd.CSNet"
55#define CSD_CSNET_REGISTRATION "com.nokia.csd.CSNet.NetworkRegistration"
56#define CSD_CSNET_OPERATOR "com.nokia.csd.CSNet.NetworkOperator"
57#define CSD_CSNET_SIGNAL "com.nokia.csd.CSNet.SignalStrength"
58
59enum net_registration_status {
60 NETWORK_REG_STATUS_HOME,
61 NETWORK_REG_STATUS_ROAMING,
62 NETWORK_REG_STATUS_OFFLINE,
63 NETWORK_REG_STATUS_SEARCHING,
64 NETWORK_REG_STATUS_NO_SIM,
65 NETWORK_REG_STATUS_POWEROFF,
66 NETWORK_REG_STATUS_POWERSAFE,
67 NETWORK_REG_STATUS_NO_COVERAGE,
68 NETWORK_REG_STATUS_REJECTED,
69 NETWORK_REG_STATUS_UNKOWN
70};
71
72/* CSD CALL plugin D-Bus definitions */
73#define CSD_CALL_BUS_NAME "com.nokia.csd.Call"
74#define CSD_CALL_INTERFACE "com.nokia.csd.Call"
75#define CSD_CALL_INSTANCE "com.nokia.csd.Call.Instance"
76#define CSD_CALL_CONFERENCE "com.nokia.csd.Call.Conference"
77#define CSD_CALL_PATH "/com/nokia/csd/call"
78#define CSD_CALL_CONFERENCE_PATH "/com/nokia/csd/call/conference"
79
80/* Call status values as exported by the CSD CALL plugin */
81#define CSD_CALL_STATUS_IDLE 0
82#define CSD_CALL_STATUS_CREATE 1
83#define CSD_CALL_STATUS_COMING 2
84#define CSD_CALL_STATUS_PROCEEDING 3
85#define CSD_CALL_STATUS_MO_ALERTING 4
86#define CSD_CALL_STATUS_MT_ALERTING 5
87#define CSD_CALL_STATUS_WAITING 6
88#define CSD_CALL_STATUS_ANSWERED 7
89#define CSD_CALL_STATUS_ACTIVE 8
90#define CSD_CALL_STATUS_MO_RELEASE 9
91#define CSD_CALL_STATUS_MT_RELEASE 10
92#define CSD_CALL_STATUS_HOLD_INITIATED 11
93#define CSD_CALL_STATUS_HOLD 12
94#define CSD_CALL_STATUS_RETRIEVE_INITIATED 13
95#define CSD_CALL_STATUS_RECONNECT_PENDING 14
96#define CSD_CALL_STATUS_TERMINATED 15
97#define CSD_CALL_STATUS_SWAP_INITIATED 16
98
99#define CALL_FLAG_NONE 0
100#define CALL_FLAG_PRESENTATION_ALLOWED 0x01
101#define CALL_FLAG_PRESENTATION_RESTRICTED 0x02
102
103/* SIM Phonebook D-Bus definitions */
104#define CSD_SIMPB_BUS_NAME "com.nokia.csd.SIM"
105#define CSD_SIMPB_INTERFACE "com.nokia.csd.SIM.Phonebook"
106#define CSD_SIMPB_PATH "/com/nokia/csd/sim/phonebook"
107
108#define CSD_SIMPB_TYPE_ADN "ADN"
109#define CSD_SIMPB_TYPE_FDN "FDN"
110#define CSD_SIMPB_TYPE_SDN "SDN"
111#define CSD_SIMPB_TYPE_VMBX "VMBX"
112#define CSD_SIMPB_TYPE_MBDN "MBDN"
113#define CSD_SIMPB_TYPE_EN "EN"
114#define CSD_SIMPB_TYPE_MSISDN "MSISDN"
115
116/* OHM plugin D-Bus definitions */
117#define OHM_BUS_NAME "com.nokia.NonGraphicFeedback1"
118#define OHM_INTERFACE "com.nokia.NonGraphicFeedback1"
119#define OHM_PATH "/com/nokia/NonGraphicFeedback1"
120
121/* tone-genenerator D-Bus definitions */
122#define TONEGEN_BUS_NAME "com.Nokia.Telephony.Tones"
123#define TONEGEN_INTERFACE "com.Nokia.Telephony.Tones"
124#define TONEGEN_PATH "/com/Nokia/Telephony/Tones"
125
126/* tone-generator DTMF definitions */
127#define DTMF_ASTERISK 10
128#define DTMF_HASHMARK 11
129#define DTMF_A 12
130#define DTMF_B 13
131#define DTMF_C 14
132#define DTMF_D 15
133
134#define FEEDBACK_TONE_DURATION 200
135
136struct csd_call {
137 char *object_path;
138 int status;
139 gboolean originating;
140 gboolean emergency;
141 gboolean on_hold;
142 gboolean conference;
143 char *number;
144 gboolean setup;
145};
146
147static struct {
148 char *operator_name;
149 uint8_t status;
150 int32_t signal_bars;
151} net = {
152 .operator_name = NULL,
153 .status = NETWORK_REG_STATUS_UNKOWN,
154 /* Init as 0 meaning inactive mode. In modem power off state
155 * can be be -1, but we treat all values as 0s regardless
156 * inactive or power off. */
157 .signal_bars = 0,
158};
159
160struct pending_req {
161 DBusPendingCall *call;
162 void *user_data;
163};
164
165static int get_property(const char *iface, const char *prop);
166
167static DBusConnection *connection = NULL;
168
169static GSList *calls = NULL;
170static GSList *watches = NULL;
171static GSList *pending = NULL;
172
173/* Reference count for determining the call indicator status */
174static GSList *active_calls = NULL;
175
176/* Queue of DTMF tones to play */
177static GSList *tones = NULL;
178static guint create_tones_timer = 0;
179
180static char *msisdn = NULL; /* Subscriber number */
181static char *vmbx = NULL; /* Voice mailbox number */
182
183/* HAL battery namespace key values */
184static int battchg_cur = -1; /* "battery.charge_level.current" */
185static int battchg_last = -1; /* "battery.charge_level.last_full" */
186static int battchg_design = -1; /* "battery.charge_level.design" */
187
188static gboolean get_calls_active = FALSE;
189
190static gboolean events_enabled = FALSE;
191
192/* Supported set of call hold operations */
193static const char *chld_str = "0,1,1x,2,2x,3,4";
194
195/* Timer for tracking call creation requests */
196static guint create_request_timer = 0;
197
198static struct indicator maemo_indicators[] =
199{
200 { "battchg", "0-5", 5, TRUE },
201 /* signal strength in terms of bars */
202 { "signal", "0-5", 0, TRUE },
203 { "service", "0,1", 0, TRUE },
204 { "call", "0,1", 0, TRUE },
205 { "callsetup", "0-3", 0, TRUE },
206 { "callheld", "0-2", 0, FALSE },
207 { "roam", "0,1", 0, TRUE },
208 { NULL }
209};
210
211static char *call_status_str[] = {
212 "IDLE",
213 "CREATE",
214 "COMING",
215 "PROCEEDING",
216 "MO_ALERTING",
217 "MT_ALERTING",
218 "WAITING",
219 "ANSWERED",
220 "ACTIVE",
221 "MO_RELEASE",
222 "MT_RELEASE",
223 "HOLD_INITIATED",
224 "HOLD",
225 "RETRIEVE_INITIATED",
226 "RECONNECT_PENDING",
227 "TERMINATED",
228 "SWAP_INITIATED",
229 "???"
230};
231
232static int send_method_call(const char *dest, const char *path,
233 const char *interface, const char *method,
234 DBusPendingCallNotifyFunction cb,
235 void *user_data, int type, ...)
236{
237 DBusMessage *msg;
238 DBusPendingCall *call;
239 va_list args;
240 struct pending_req *req;
241
242 msg = dbus_message_new_method_call(dest, path, interface, method);
243 if (!msg) {
244 error("Unable to allocate new D-Bus %s message", method);
245 return -ENOMEM;
246 }
247
248 va_start(args, type);
249
250 if (!dbus_message_append_args_valist(msg, type, args)) {
251 dbus_message_unref(msg);
252 va_end(args);
253 return -EIO;
254 }
255
256 va_end(args);
257
258 if (!cb) {
259 g_dbus_send_message(connection, msg);
260 return 0;
261 }
262
263 if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) {
264 error("Sending %s failed", method);
265 dbus_message_unref(msg);
266 return -EIO;
267 }
268
269 dbus_pending_call_set_notify(call, cb, user_data, NULL);
270
271 req = g_new0(struct pending_req, 1);
272 req->call = call;
273 req->user_data = user_data;
274
275 pending = g_slist_prepend(pending, req);
276 dbus_message_unref(msg);
277
278 return 0;
279}
280
281static struct csd_call *find_call(const char *path)
282{
283 GSList *l;
284
285 for (l = calls; l != NULL; l = l->next) {
286 struct csd_call *call = l->data;
287
288 if (g_str_equal(call->object_path, path))
289 return call;
290 }
291
292 return NULL;
293}
294
295static struct csd_call *find_non_held_call(void)
296{
297 GSList *l;
298
299 for (l = calls; l != NULL; l = l->next) {
300 struct csd_call *call = l->data;
301
302 if (call->status == CSD_CALL_STATUS_IDLE)
303 continue;
304
305 if (call->status != CSD_CALL_STATUS_HOLD)
306 return call;
307 }
308
309 return NULL;
310}
311
312static struct csd_call *find_non_idle_call(void)
313{
314 GSList *l;
315
316 for (l = calls; l != NULL; l = l->next) {
317 struct csd_call *call = l->data;
318
319 if (call->status != CSD_CALL_STATUS_IDLE)
320 return call;
321 }
322
323 return NULL;
324}
325
326static struct csd_call *find_call_with_status(int status)
327{
328 GSList *l;
329
330 for (l = calls; l != NULL; l = l->next) {
331 struct csd_call *call = l->data;
332
333 if (call->status == status)
334 return call;
335 }
336
337 return NULL;
338}
339
340static int release_conference(void)
341{
342 DBusMessage *msg;
343
344 DBG("telephony-maemo6: releasing conference call");
345
346 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
347 CSD_CALL_CONFERENCE_PATH,
348 CSD_CALL_INSTANCE,
349 "Release");
350 if (!msg) {
351 error("Unable to allocate new D-Bus message");
352 return -ENOMEM;
353 }
354
355 g_dbus_send_message(connection, msg);
356
357 return 0;
358}
359
360static int release_call(struct csd_call *call)
361{
362 DBusMessage *msg;
363
364 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
365 call->object_path,
366 CSD_CALL_INSTANCE,
367 "Release");
368 if (!msg) {
369 error("Unable to allocate new D-Bus message");
370 return -ENOMEM;
371 }
372
373 g_dbus_send_message(connection, msg);
374
375 return 0;
376}
377
378static int answer_call(struct csd_call *call)
379{
380 DBusMessage *msg;
381
382 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
383 call->object_path,
384 CSD_CALL_INSTANCE,
385 "Answer");
386 if (!msg) {
387 error("Unable to allocate new D-Bus message");
388 return -ENOMEM;
389 }
390
391 g_dbus_send_message(connection, msg);
392
393 return 0;
394}
395
396static struct pending_req *find_request(const DBusPendingCall *call)
397{
398 GSList *l;
399
400 for (l = pending; l; l = l->next) {
401 struct pending_req *req = l->data;
402
403 if (req->call == call)
404 return req;
405 }
406
407 return NULL;
408}
409
410static void pending_req_finalize(void *data)
411{
412 struct pending_req *req = data;
413
414 if (!dbus_pending_call_get_completed(req->call))
415 dbus_pending_call_cancel(req->call);
416
417 dbus_pending_call_unref(req->call);
418 g_free(req);
419}
420
421static void remove_pending(DBusPendingCall *call)
422{
423 struct pending_req *req = find_request(call);
424
425 pending = g_slist_remove(pending, req);
426 pending_req_finalize(req);
427}
428
429static void stop_ringtone_reply(DBusPendingCall *call, void *user_data)
430{
431 struct csd_call *coming = user_data;
432
433 remove_pending(call);
434 answer_call(coming);
435}
436
437static int stop_ringtone_and_answer(struct csd_call *call)
438{
439 int ret;
440
441 ret = send_method_call(OHM_BUS_NAME, OHM_PATH,
442 OHM_INTERFACE, "StopRingtone",
443 stop_ringtone_reply, call,
444 DBUS_TYPE_INVALID);
445 if (ret < 0)
446 return answer_call(call);
447
448 return 0;
449}
450
451static int split_call(struct csd_call *call)
452{
453 DBusMessage *msg;
454
455 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME,
456 call->object_path,
457 CSD_CALL_INSTANCE,
458 "Split");
459 if (!msg) {
460 error("Unable to allocate new D-Bus message");
461 return -ENOMEM;
462 }
463
464 g_dbus_send_message(connection, msg);
465
466 return 0;
467}
468
469static int unhold_call(struct csd_call *call)
470{
471 DBusMessage *msg;
472
473 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
474 CSD_CALL_INTERFACE,
475 "Unhold");
476 if (!msg) {
477 error("Unable to allocate new D-Bus message");
478 return -ENOMEM;
479 }
480
481 g_dbus_send_message(connection, msg);
482
483 return 0;
484}
485
486static int hold_call(struct csd_call *call)
487{
488 DBusMessage *msg;
489
490 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
491 CSD_CALL_INTERFACE,
492 "Hold");
493 if (!msg) {
494 error("Unable to allocate new D-Bus message");
495 return -ENOMEM;
496 }
497
498 g_dbus_send_message(connection, msg);
499
500 return 0;
501}
502
503static int swap_calls(void)
504{
505 DBusMessage *msg;
506
507 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
508 CSD_CALL_INTERFACE,
509 "Swap");
510 if (!msg) {
511 error("Unable to allocate new D-Bus message");
512 return -ENOMEM;
513 }
514
515 g_dbus_send_message(connection, msg);
516
517 return 0;
518}
519
520static int create_conference(void)
521{
522 DBusMessage *msg;
523
524 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
525 CSD_CALL_INTERFACE,
526 "Conference");
527 if (!msg) {
528 error("Unable to allocate new D-Bus message");
529 return -ENOMEM;
530 }
531
532 g_dbus_send_message(connection, msg);
533
534 return 0;
535}
536
537static int call_transfer(void)
538{
539 DBusMessage *msg;
540
541 msg = dbus_message_new_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
542 CSD_CALL_INTERFACE,
543 "Transfer");
544 if (!msg) {
545 error("Unable to allocate new D-Bus message");
546 return -ENOMEM;
547 }
548
549 g_dbus_send_message(connection, msg);
550
551 return 0;
552}
553
554static int number_type(const char *number)
555{
556 if (number == NULL)
557 return NUMBER_TYPE_TELEPHONY;
558
559 if (number[0] == '+' || strncmp(number, "00", 2) == 0)
560 return NUMBER_TYPE_INTERNATIONAL;
561
562 return NUMBER_TYPE_TELEPHONY;
563}
564
565void telephony_device_connected(void *telephony_device)
566{
567 struct csd_call *coming;
568
569 DBG("telephony-maemo6: device %p connected", telephony_device);
570
571 coming = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
572 if (coming) {
573 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE))
574 telephony_call_waiting_ind(coming->number,
575 number_type(coming->number));
576 else
577 telephony_incoming_call_ind(coming->number,
578 number_type(coming->number));
579 }
580}
581
582static void remove_pending_by_data(gpointer data, gpointer user_data)
583{
584 struct pending_req *req = data;
585
586 if (req->user_data == user_data) {
587 pending = g_slist_remove(pending, req);
588 pending_req_finalize(req);
589 }
590}
591
592void telephony_device_disconnected(void *telephony_device)
593{
594 DBG("telephony-maemo6: device %p disconnected", telephony_device);
595 events_enabled = FALSE;
596
597 g_slist_foreach(pending, remove_pending_by_data, telephony_device);
598}
599
600void telephony_event_reporting_req(void *telephony_device, int ind)
601{
602 events_enabled = ind == 1 ? TRUE : FALSE;
603
604 telephony_event_reporting_rsp(telephony_device, CME_ERROR_NONE);
605}
606
607void telephony_response_and_hold_req(void *telephony_device, int rh)
608{
609 telephony_response_and_hold_rsp(telephony_device,
610 CME_ERROR_NOT_SUPPORTED);
611}
612
613void telephony_terminate_call_req(void *telephony_device)
614{
615 struct csd_call *call;
616 struct csd_call *alerting;
617 int err;
618
619 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
620 if (!call)
621 call = find_non_idle_call();
622
623 if (!call) {
624 error("No active call");
625 telephony_terminate_call_rsp(telephony_device,
626 CME_ERROR_NOT_ALLOWED);
627 return;
628 }
629
630 alerting = find_call_with_status(CSD_CALL_STATUS_MO_ALERTING);
631 if (call->on_hold && alerting)
632 err = release_call(alerting);
633 else if (call->conference)
634 err = release_conference();
635 else
636 err = release_call(call);
637
638 if (err < 0)
639 telephony_terminate_call_rsp(telephony_device,
640 CME_ERROR_AG_FAILURE);
641 else
642 telephony_terminate_call_rsp(telephony_device, CME_ERROR_NONE);
643}
644
645void telephony_answer_call_req(void *telephony_device)
646{
647 struct csd_call *call;
648
649 call = find_call_with_status(CSD_CALL_STATUS_COMING);
650 if (!call)
651 call = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
652
653 if (!call)
654 call = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
655
656 if (!call)
657 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
658
659 if (!call) {
660 telephony_answer_call_rsp(telephony_device,
661 CME_ERROR_NOT_ALLOWED);
662 return;
663 }
664
665 if (stop_ringtone_and_answer(call) < 0)
666 telephony_answer_call_rsp(telephony_device,
667 CME_ERROR_AG_FAILURE);
668 else
669 telephony_answer_call_rsp(telephony_device, CME_ERROR_NONE);
670}
671
672static void create_call_reply(DBusPendingCall *call, void *user_data)
673{
674 DBusError err;
675 DBusMessage *reply;
676 void *telephony_device = user_data;
677
678 reply = dbus_pending_call_steal_reply(call);
679
680 dbus_error_init(&err);
681 if (dbus_set_error_from_message(&err, reply)) {
682 error("csd replied with an error: %s, %s",
683 err.name, err.message);
684 if (g_strcmp0(err.name,
685 "com.nokia.csd.Call.Error.CSInactive") == 0)
686 telephony_dial_number_rsp(telephony_device,
687 CME_ERROR_NO_NETWORK_SERVICE);
688 else
689 telephony_dial_number_rsp(telephony_device,
690 CME_ERROR_AG_FAILURE);
691 dbus_error_free(&err);
692 } else
693 telephony_dial_number_rsp(telephony_device, CME_ERROR_NONE);
694
695 dbus_message_unref(reply);
696 remove_pending(call);
697}
698
699void telephony_last_dialed_number_req(void *telephony_device)
700{
701 int ret;
702
703 DBG("telephony-maemo6: last dialed number request");
704
705 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
706 CSD_CALL_INTERFACE, "CreateFromLast",
707 create_call_reply, telephony_device,
708 DBUS_TYPE_INVALID);
709 if (ret < 0)
710 telephony_dial_number_rsp(telephony_device,
711 CME_ERROR_AG_FAILURE);
712}
713
714static const char *memory_dial_lookup(int location)
715{
716 if (location == 1)
717 return vmbx;
718 else
719 return NULL;
720}
721
722void telephony_dial_number_req(void *telephony_device, const char *number)
723{
724 int ret;
725
726 DBG("telephony-maemo6: dial request to %s", number);
727
728 if (strncmp(number, "*31#", 4) == 0)
729 number += 4;
730 else if (strncmp(number, "#31#", 4) == 0)
731 number += 4;
732 else if (number[0] == '>') {
733 const char *location = &number[1];
734
735 number = memory_dial_lookup(strtol(&number[1], NULL, 0));
736 if (!number) {
737 error("No number at memory location %s", location);
738 telephony_dial_number_rsp(telephony_device,
739 CME_ERROR_INVALID_INDEX);
740 return;
741 }
742 }
743
744 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
745 CSD_CALL_INTERFACE, "Create",
746 create_call_reply, telephony_device,
747 DBUS_TYPE_STRING, &number,
748 DBUS_TYPE_INVALID);
749 if (ret < 0)
750 telephony_dial_number_rsp(telephony_device,
751 CME_ERROR_AG_FAILURE);
752}
753
754static void start_dtmf_reply(DBusPendingCall *call, void *user_data)
755{
756 DBusError err;
757 DBusMessage *reply;
758
759 reply = dbus_pending_call_steal_reply(call);
760
761 dbus_error_init(&err);
762 if (dbus_set_error_from_message(&err, reply)) {
763 error("csd replied with an error: %s, %s",
764 err.name, err.message);
765
766 dbus_error_free(&err);
767 } else
768 send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
769 CSD_CALL_INTERFACE, "StopDTMF",
770 NULL, NULL,
771 DBUS_TYPE_INVALID);
772
773 dbus_message_unref(reply);
774 remove_pending(call);
775}
776
777static void start_dtmf(void *telephony_device, char tone)
778{
779 int ret;
780
781 /*
782 * Stop tone immediately, modem will place it in queue and play
783 * required time.
784 */
785 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
786 CSD_CALL_INTERFACE, "StartDTMF",
787 start_dtmf_reply, NULL,
788 DBUS_TYPE_BYTE, &tone,
789 DBUS_TYPE_INVALID);
790 if (ret < 0) {
791 telephony_transmit_dtmf_rsp(telephony_device,
792 CME_ERROR_AG_FAILURE);
793 return;
794 }
795
796 telephony_transmit_dtmf_rsp(telephony_device, CME_ERROR_NONE);
797}
798
799static int tonegen_startevent(char tone)
800{
801 int ret;
802 dbus_uint32_t event_tone;
803 dbus_int32_t dbm0 = -15;
804 dbus_uint32_t duration = 150;
805
806 switch (tone) {
807 case '*':
808 event_tone = DTMF_ASTERISK;
809 break;
810 case '#':
811 event_tone = DTMF_HASHMARK;
812 break;
813 case 'A':
814 event_tone = DTMF_A;
815 break;
816 case 'B':
817 event_tone = DTMF_B;
818 break;
819 case 'C':
820 event_tone = DTMF_C;
821 break;
822 case 'D':
823 event_tone = DTMF_D;
824 break;
825 default:
826 ret = g_ascii_digit_value(tone);
827 if (ret < 0)
828 return -EINVAL;
829 event_tone = ret;
830 }
831
832 ret = send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
833 TONEGEN_INTERFACE, "StartEventTone",
834 NULL, NULL,
835 DBUS_TYPE_UINT32, &event_tone,
836 DBUS_TYPE_INT32, &dbm0,
837 DBUS_TYPE_UINT32, &duration,
838 DBUS_TYPE_INVALID);
839 return ret;
840}
841
842static gboolean stop_feedback_tone(gpointer user_data)
843{
844 if (g_slist_length(tones) > 0) {
845 gpointer ptone;
846 int ret;
847
848 send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
849 TONEGEN_INTERFACE, "StopTone",
850 NULL, NULL,
851 DBUS_TYPE_INVALID);
852
853 ptone = g_slist_nth_data(tones, 0);
854 tones = g_slist_remove(tones, ptone);
855
856 ret = tonegen_startevent(GPOINTER_TO_UINT(ptone));
857 if (ret < 0)
858 goto done;
859
860 return TRUE;
861 }
862done:
863 return FALSE;
864}
865
866static void tones_timer_notify(gpointer data)
867{
868 send_method_call(TONEGEN_BUS_NAME, TONEGEN_PATH,
869 TONEGEN_INTERFACE, "StopTone",
870 NULL, NULL,
871 DBUS_TYPE_INVALID);
872 g_slist_free(tones);
873 tones = NULL;
874
875 create_tones_timer = 0;
876}
877
878static void start_feedback_tone(char tone)
879{
880 if (!create_tones_timer) {
881 int ret;
882
883 ret = tonegen_startevent(tone);
884 if (ret < 0)
885 return;
886
887 create_tones_timer = g_timeout_add_full(G_PRIORITY_DEFAULT,
888 FEEDBACK_TONE_DURATION,
889 stop_feedback_tone,
890 NULL,
891 tones_timer_notify);
892 } else {
893 glong dtmf_tone = tone;
894
895 DBG("add %c to queue", tone);
896 tones = g_slist_append(tones, GUINT_TO_POINTER(dtmf_tone));
897 }
898}
899
900void telephony_transmit_dtmf_req(void *telephony_device, char tone)
901{
902 DBG("telephony-maemo6: transmit dtmf: %c", tone);
903
904 start_dtmf(telephony_device, tone);
905
906 if (!find_call_with_status(CSD_CALL_STATUS_ACTIVE))
907 error("No active call");
908 else
909 start_feedback_tone(tone);
910}
911
912void telephony_subscriber_number_req(void *telephony_device)
913{
914 DBG("telephony-maemo6: subscriber number request");
915 if (msisdn)
916 telephony_subscriber_number_ind(msisdn,
917 number_type(msisdn),
918 SUBSCRIBER_SERVICE_VOICE);
919 telephony_subscriber_number_rsp(telephony_device, CME_ERROR_NONE);
920}
921
922static int csd_status_to_hfp(struct csd_call *call)
923{
924 switch (call->status) {
925 case CSD_CALL_STATUS_IDLE:
926 case CSD_CALL_STATUS_MO_RELEASE:
927 case CSD_CALL_STATUS_MT_RELEASE:
928 case CSD_CALL_STATUS_TERMINATED:
929 return -1;
930 case CSD_CALL_STATUS_CREATE:
931 return CALL_STATUS_DIALING;
932 case CSD_CALL_STATUS_WAITING:
933 return CALL_STATUS_WAITING;
934 case CSD_CALL_STATUS_PROCEEDING:
935 /* PROCEEDING can happen in outgoing/incoming */
936 if (call->originating)
937 return CALL_STATUS_DIALING;
938
939 /*
940 * PROCEEDING is followed by WAITING CSD status, therefore
941 * second incoming call status indication is set immediately
942 * to waiting.
943 */
944 if (g_slist_length(active_calls) > 0)
945 return CALL_STATUS_WAITING;
946
947 return CALL_STATUS_INCOMING;
948 case CSD_CALL_STATUS_COMING:
949 if (g_slist_length(active_calls) > 0)
950 return CALL_STATUS_WAITING;
951
952 return CALL_STATUS_INCOMING;
953 case CSD_CALL_STATUS_MO_ALERTING:
954 return CALL_STATUS_ALERTING;
955 case CSD_CALL_STATUS_MT_ALERTING:
956 return CALL_STATUS_INCOMING;
957 case CSD_CALL_STATUS_ANSWERED:
958 case CSD_CALL_STATUS_ACTIVE:
959 case CSD_CALL_STATUS_RECONNECT_PENDING:
960 case CSD_CALL_STATUS_SWAP_INITIATED:
961 case CSD_CALL_STATUS_HOLD_INITIATED:
962 return CALL_STATUS_ACTIVE;
963 case CSD_CALL_STATUS_RETRIEVE_INITIATED:
964 case CSD_CALL_STATUS_HOLD:
965 return CALL_STATUS_HELD;
966 default:
967 return -1;
968 }
969}
970
971void telephony_list_current_calls_req(void *telephony_device)
972{
973 GSList *l;
974 int i;
975
976 DBG("telephony-maemo6: list current calls request");
977
978 for (l = calls, i = 1; l != NULL; l = l->next, i++) {
979 struct csd_call *call = l->data;
980 int status, direction, multiparty;
981
982 status = csd_status_to_hfp(call);
983 if (status < 0)
984 continue;
985
986 direction = call->originating ?
987 CALL_DIR_OUTGOING : CALL_DIR_INCOMING;
988
989 multiparty = call->conference ?
990 CALL_MULTIPARTY_YES : CALL_MULTIPARTY_NO;
991
992 telephony_list_current_call_ind(i, direction, status,
993 CALL_MODE_VOICE, multiparty,
994 call->number,
995 number_type(call->number));
996 }
997
998 telephony_list_current_calls_rsp(telephony_device, CME_ERROR_NONE);
999}
1000
1001void telephony_operator_selection_req(void *telephony_device)
1002{
1003 telephony_operator_selection_ind(OPERATOR_MODE_AUTO,
1004 net.operator_name ? net.operator_name : "");
1005 telephony_operator_selection_rsp(telephony_device, CME_ERROR_NONE);
1006}
1007
1008static void foreach_call_with_status(int status,
1009 int (*func)(struct csd_call *call))
1010{
1011 GSList *l;
1012
1013 for (l = calls; l != NULL; l = l->next) {
1014 struct csd_call *call = l->data;
1015
1016 if (call->status == status)
1017 func(call);
1018 }
1019}
1020
1021void telephony_call_hold_req(void *telephony_device, const char *cmd)
1022{
1023 const char *idx;
1024 struct csd_call *call;
1025 int err = 0;
1026
1027 DBG("telephony-maemo6: got call hold request %s", cmd);
1028
1029 if (strlen(cmd) > 1)
1030 idx = &cmd[1];
1031 else
1032 idx = NULL;
1033
1034 if (idx)
1035 call = g_slist_nth_data(calls, strtol(idx, NULL, 0) - 1);
1036 else
1037 call = NULL;
1038
1039 switch (cmd[0]) {
1040 case '0':
1041 if (find_call_with_status(CSD_CALL_STATUS_WAITING))
1042 foreach_call_with_status(CSD_CALL_STATUS_WAITING,
1043 release_call);
1044 else
1045 foreach_call_with_status(CSD_CALL_STATUS_HOLD,
1046 release_call);
1047 break;
1048 case '1':
1049 if (idx) {
1050 if (call)
1051 err = release_call(call);
1052 break;
1053 }
1054 foreach_call_with_status(CSD_CALL_STATUS_ACTIVE, release_call);
1055 call = find_call_with_status(CSD_CALL_STATUS_WAITING);
1056 if (call)
1057 err = answer_call(call);
1058 break;
1059 case '2':
1060 if (idx) {
1061 if (call)
1062 err = split_call(call);
1063 } else {
1064 struct csd_call *held, *wait;
1065
1066 call = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1067 held = find_call_with_status(CSD_CALL_STATUS_HOLD);
1068 wait = find_call_with_status(CSD_CALL_STATUS_WAITING);
1069
1070 if (wait)
1071 err = answer_call(wait);
1072 else if (call && held)
1073 err = swap_calls();
1074 else {
1075 if (call)
1076 err = hold_call(call);
1077 if (held)
1078 err = unhold_call(held);
1079 }
1080 }
1081 break;
1082 case '3':
1083 if (find_call_with_status(CSD_CALL_STATUS_HOLD) ||
1084 find_call_with_status(CSD_CALL_STATUS_WAITING))
1085 err = create_conference();
1086 break;
1087 case '4':
1088 err = call_transfer();
1089 break;
1090 default:
1091 DBG("Unknown call hold request");
1092 break;
1093 }
1094
1095 if (err)
1096 telephony_call_hold_rsp(telephony_device,
1097 CME_ERROR_AG_FAILURE);
1098 else
1099 telephony_call_hold_rsp(telephony_device, CME_ERROR_NONE);
1100}
1101
1102void telephony_nr_and_ec_req(void *telephony_device, gboolean enable)
1103{
1104 DBG("telephony-maemo6: got %s NR and EC request",
1105 enable ? "enable" : "disable");
1106 telephony_nr_and_ec_rsp(telephony_device, CME_ERROR_NONE);
1107}
1108
1109void telephony_key_press_req(void *telephony_device, const char *keys)
1110{
1111 struct csd_call *active, *waiting;
1112 int err;
1113
1114 DBG("telephony-maemo6: got key press request for %s", keys);
1115
1116 waiting = find_call_with_status(CSD_CALL_STATUS_COMING);
1117 if (!waiting)
1118 waiting = find_call_with_status(CSD_CALL_STATUS_MT_ALERTING);
1119 if (!waiting)
1120 waiting = find_call_with_status(CSD_CALL_STATUS_PROCEEDING);
1121
1122 active = find_call_with_status(CSD_CALL_STATUS_ACTIVE);
1123
1124 if (waiting)
1125 err = answer_call(waiting);
1126 else if (active)
1127 err = release_call(active);
1128 else
1129 err = 0;
1130
1131 if (err < 0)
1132 telephony_key_press_rsp(telephony_device,
1133 CME_ERROR_AG_FAILURE);
1134 else
1135 telephony_key_press_rsp(telephony_device, CME_ERROR_NONE);
1136}
1137
1138void telephony_voice_dial_req(void *telephony_device, gboolean enable)
1139{
1140 DBG("telephony-maemo6: got %s voice dial request",
1141 enable ? "enable" : "disable");
1142
1143 telephony_voice_dial_rsp(telephony_device, CME_ERROR_NOT_SUPPORTED);
1144}
1145
1146static void handle_incoming_call(DBusMessage *msg)
1147{
1148 const char *number, *call_path;
1149 struct csd_call *call;
1150
1151 if (!dbus_message_get_args(msg, NULL,
1152 DBUS_TYPE_OBJECT_PATH, &call_path,
1153 DBUS_TYPE_STRING, &number,
1154 DBUS_TYPE_INVALID)) {
1155 error("Unexpected parameters in Call.Coming() signal");
1156 return;
1157 }
1158
1159 call = find_call(call_path);
1160 if (!call) {
1161 error("Didn't find any matching call object for %s",
1162 call_path);
1163 return;
1164 }
1165
1166 DBG("Incoming call to %s from number %s", call_path, number);
1167
1168 g_free(call->number);
1169 call->number = g_strdup(number);
1170
1171 if (find_call_with_status(CSD_CALL_STATUS_ACTIVE) ||
1172 find_call_with_status(CSD_CALL_STATUS_HOLD))
1173 telephony_call_waiting_ind(call->number,
1174 number_type(call->number));
1175 else
1176 telephony_incoming_call_ind(call->number,
1177 number_type(call->number));
1178
1179 telephony_update_indicator(maemo_indicators, "callsetup",
1180 EV_CALLSETUP_INCOMING);
1181}
1182
1183static void handle_outgoing_call(DBusMessage *msg)
1184{
1185 const char *number, *call_path;
1186 struct csd_call *call;
1187
1188 if (!dbus_message_get_args(msg, NULL,
1189 DBUS_TYPE_OBJECT_PATH, &call_path,
1190 DBUS_TYPE_STRING, &number,
1191 DBUS_TYPE_INVALID)) {
1192 error("Unexpected parameters in Call.Created() signal");
1193 return;
1194 }
1195
1196 call = find_call(call_path);
1197 if (!call) {
1198 error("Didn't find any matching call object for %s",
1199 call_path);
1200 return;
1201 }
1202
1203 DBG("Outgoing call from %s to number %s", call_path, number);
1204
1205 g_free(call->number);
1206 call->number = g_strdup(number);
1207
1208 if (create_request_timer) {
1209 g_source_remove(create_request_timer);
1210 create_request_timer = 0;
1211 }
1212}
1213
1214static gboolean create_timeout(gpointer user_data)
1215{
1216 telephony_update_indicator(maemo_indicators, "callsetup",
1217 EV_CALLSETUP_INACTIVE);
1218 create_request_timer = 0;
1219 return FALSE;
1220}
1221
1222static void handle_create_requested(DBusMessage *msg)
1223{
1224 DBG("Call.CreateRequested()");
1225
1226 if (create_request_timer)
1227 g_source_remove(create_request_timer);
1228
1229 create_request_timer = g_timeout_add_seconds(5, create_timeout, NULL);
1230
1231 telephony_update_indicator(maemo_indicators, "callsetup",
1232 EV_CALLSETUP_OUTGOING);
1233}
1234
1235static void call_set_status(struct csd_call *call, dbus_uint32_t status)
1236{
1237 dbus_uint32_t prev_status;
1238 int callheld = telephony_get_indicator(maemo_indicators, "callheld");
1239
1240 prev_status = call->status;
1241 DBG("Call %s changed from %s to %s", call->object_path,
1242 call_status_str[prev_status], call_status_str[status]);
1243
1244 if (prev_status == status) {
1245 DBG("Ignoring CSD Call state change to existing state");
1246 return;
1247 }
1248
1249 call->status = (int) status;
1250
1251 switch (status) {
1252 case CSD_CALL_STATUS_IDLE:
1253 if (call->setup) {
1254 telephony_update_indicator(maemo_indicators,
1255 "callsetup",
1256 EV_CALLSETUP_INACTIVE);
1257 if (!call->originating)
1258 telephony_calling_stopped_ind();
1259 }
1260
1261 g_free(call->number);
1262 call->number = NULL;
1263 call->originating = FALSE;
1264 call->emergency = FALSE;
1265 call->on_hold = FALSE;
1266 call->conference = FALSE;
1267 call->setup = FALSE;
1268 break;
1269 case CSD_CALL_STATUS_CREATE:
1270 call->originating = TRUE;
1271 call->setup = TRUE;
1272 break;
1273 case CSD_CALL_STATUS_COMING:
1274 call->originating = FALSE;
1275 call->setup = TRUE;
1276 break;
1277 case CSD_CALL_STATUS_PROCEEDING:
1278 break;
1279 case CSD_CALL_STATUS_MO_ALERTING:
1280 telephony_update_indicator(maemo_indicators, "callsetup",
1281 EV_CALLSETUP_ALERTING);
1282 break;
1283 case CSD_CALL_STATUS_MT_ALERTING:
1284 /* Some headsets expect incoming call notification before they
1285 * can send ATA command. When call changed status from waiting
1286 * to alerting we need to send missing notification. Otherwise
1287 * headsets like Nokia BH-108 or BackBeat 903 are unable to
1288 * answer incoming call that was previously waiting. */
1289 if (prev_status == CSD_CALL_STATUS_WAITING)
1290 telephony_incoming_call_ind(call->number,
1291 number_type(call->number));
1292 break;
1293 case CSD_CALL_STATUS_WAITING:
1294 break;
1295 case CSD_CALL_STATUS_ANSWERED:
1296 break;
1297 case CSD_CALL_STATUS_ACTIVE:
1298 if (call->on_hold) {
1299 call->on_hold = FALSE;
1300 if (find_call_with_status(CSD_CALL_STATUS_HOLD))
1301 telephony_update_indicator(maemo_indicators,
1302 "callheld",
1303 EV_CALLHELD_MULTIPLE);
1304 else
1305 telephony_update_indicator(maemo_indicators,
1306 "callheld",
1307 EV_CALLHELD_NONE);
1308 } else {
1309 if (!g_slist_find(active_calls, call))
1310 active_calls = g_slist_prepend(active_calls, call);
1311 if (g_slist_length(active_calls) == 1)
1312 telephony_update_indicator(maemo_indicators,
1313 "call",
1314 EV_CALL_ACTIVE);
1315 /* Upgrade callheld status if necessary */
1316 if (callheld == EV_CALLHELD_ON_HOLD)
1317 telephony_update_indicator(maemo_indicators,
1318 "callheld",
1319 EV_CALLHELD_MULTIPLE);
1320 telephony_update_indicator(maemo_indicators,
1321 "callsetup",
1322 EV_CALLSETUP_INACTIVE);
1323 if (!call->originating)
1324 telephony_calling_stopped_ind();
1325 call->setup = FALSE;
1326 }
1327 break;
1328 case CSD_CALL_STATUS_MO_RELEASE:
1329 case CSD_CALL_STATUS_MT_RELEASE:
1330 active_calls = g_slist_remove(active_calls, call);
1331 if (g_slist_length(active_calls) == 0)
1332 telephony_update_indicator(maemo_indicators, "call",
1333 EV_CALL_INACTIVE);
1334
1335 if (create_tones_timer)
1336 g_source_remove(create_tones_timer);
1337 break;
1338 case CSD_CALL_STATUS_HOLD_INITIATED:
1339 break;
1340 case CSD_CALL_STATUS_HOLD:
1341 call->on_hold = TRUE;
1342 if (find_non_held_call())
1343 telephony_update_indicator(maemo_indicators,
1344 "callheld",
1345 EV_CALLHELD_MULTIPLE);
1346 else
1347 telephony_update_indicator(maemo_indicators,
1348 "callheld",
1349 EV_CALLHELD_ON_HOLD);
1350 break;
1351 case CSD_CALL_STATUS_RETRIEVE_INITIATED:
1352 break;
1353 case CSD_CALL_STATUS_RECONNECT_PENDING:
1354 break;
1355 case CSD_CALL_STATUS_TERMINATED:
1356 if (call->on_hold &&
1357 !find_call_with_status(CSD_CALL_STATUS_HOLD)) {
1358 telephony_update_indicator(maemo_indicators,
1359 "callheld",
1360 EV_CALLHELD_NONE);
1361 return;
1362 }
1363
1364 if (callheld == EV_CALLHELD_MULTIPLE &&
1365 find_call_with_status(CSD_CALL_STATUS_HOLD) &&
1366 !find_call_with_status(CSD_CALL_STATUS_ACTIVE))
1367 telephony_update_indicator(maemo_indicators,
1368 "callheld",
1369 EV_CALLHELD_ON_HOLD);
1370 break;
1371 case CSD_CALL_STATUS_SWAP_INITIATED:
1372 break;
1373 default:
1374 error("Unknown call status %u", status);
1375 break;
1376 }
1377}
1378
1379static void handle_call_status(DBusMessage *msg, const char *call_path)
1380{
1381 struct csd_call *call;
1382 dbus_uint32_t status, cause_type, cause;
1383
1384 if (!dbus_message_get_args(msg, NULL,
1385 DBUS_TYPE_UINT32, &status,
1386 DBUS_TYPE_UINT32, &cause_type,
1387 DBUS_TYPE_UINT32, &cause,
1388 DBUS_TYPE_INVALID)) {
1389 error("Unexpected paramters in Instance.CallStatus() signal");
1390 return;
1391 }
1392
1393 call = find_call(call_path);
1394 if (!call) {
1395 error("Didn't find any matching call object for %s",
1396 call_path);
1397 return;
1398 }
1399
1400 if (status > 16) {
1401 error("Invalid call status %u", status);
1402 return;
1403 }
1404
1405 call_set_status(call, status);
1406}
1407
1408static void handle_conference(DBusMessage *msg, gboolean joined)
1409{
1410 const char *path;
1411 struct csd_call *call;
1412
1413 if (!dbus_message_get_args(msg, NULL,
1414 DBUS_TYPE_OBJECT_PATH, &path,
1415 DBUS_TYPE_INVALID)) {
1416 error("Unexpected parameters in Conference.%s",
1417 dbus_message_get_member(msg));
1418 return;
1419 }
1420
1421 call = find_call(path);
1422 if (!call) {
1423 error("Conference signal for unknown call %s", path);
1424 return;
1425 }
1426
1427 DBG("Call %s %s the conference", path, joined ? "joined" : "left");
1428
1429 call->conference = joined;
1430}
1431
1432static uint8_t str2status(const char *state)
1433{
1434 if (g_strcmp0(state, "Home") == 0)
1435 return NETWORK_REG_STATUS_HOME;
1436 else if (g_strcmp0(state, "Roaming") == 0)
1437 return NETWORK_REG_STATUS_ROAMING;
1438 else if (g_strcmp0(state, "Offline") == 0)
1439 return NETWORK_REG_STATUS_OFFLINE;
1440 else if (g_strcmp0(state, "Searching") == 0)
1441 return NETWORK_REG_STATUS_SEARCHING;
1442 else if (g_strcmp0(state, "NoSim") == 0)
1443 return NETWORK_REG_STATUS_NO_SIM;
1444 else if (g_strcmp0(state, "Poweroff") == 0)
1445 return NETWORK_REG_STATUS_POWEROFF;
1446 else if (g_strcmp0(state, "Powersafe") == 0)
1447 return NETWORK_REG_STATUS_POWERSAFE;
1448 else if (g_strcmp0(state, "NoCoverage") == 0)
1449 return NETWORK_REG_STATUS_NO_COVERAGE;
1450 else if (g_strcmp0(state, "Reject") == 0)
1451 return NETWORK_REG_STATUS_REJECTED;
1452 else
1453 return NETWORK_REG_STATUS_UNKOWN;
1454}
1455
1456static void update_registration_status(const char *status)
1457{
1458 uint8_t new_status;
1459
1460 new_status = str2status(status);
1461
1462 if (net.status == new_status)
1463 return;
1464
1465 switch (new_status) {
1466 case NETWORK_REG_STATUS_HOME:
1467 telephony_update_indicator(maemo_indicators, "roam",
1468 EV_ROAM_INACTIVE);
1469 if (net.status > NETWORK_REG_STATUS_ROAMING)
1470 telephony_update_indicator(maemo_indicators,
1471 "service",
1472 EV_SERVICE_PRESENT);
1473 break;
1474 case NETWORK_REG_STATUS_ROAMING:
1475 telephony_update_indicator(maemo_indicators, "roam",
1476 EV_ROAM_ACTIVE);
1477 if (net.status > NETWORK_REG_STATUS_ROAMING)
1478 telephony_update_indicator(maemo_indicators,
1479 "service",
1480 EV_SERVICE_PRESENT);
1481 break;
1482 case NETWORK_REG_STATUS_OFFLINE:
1483 case NETWORK_REG_STATUS_SEARCHING:
1484 case NETWORK_REG_STATUS_NO_SIM:
1485 case NETWORK_REG_STATUS_POWEROFF:
1486 case NETWORK_REG_STATUS_POWERSAFE:
1487 case NETWORK_REG_STATUS_NO_COVERAGE:
1488 case NETWORK_REG_STATUS_REJECTED:
1489 case NETWORK_REG_STATUS_UNKOWN:
1490 if (net.status < NETWORK_REG_STATUS_OFFLINE)
1491 telephony_update_indicator(maemo_indicators,
1492 "service",
1493 EV_SERVICE_NONE);
1494 break;
1495 }
1496
1497 net.status = new_status;
1498
1499 DBG("telephony-maemo6: registration status changed: %s", status);
1500}
1501
1502static void handle_registration_changed(DBusMessage *msg)
1503{
1504 const char *status;
1505
1506 if (!dbus_message_get_args(msg, NULL,
1507 DBUS_TYPE_STRING, &status,
1508 DBUS_TYPE_INVALID)) {
1509 error("Unexpected parameters in RegistrationChanged");
1510 return;
1511 }
1512
1513 update_registration_status(status);
1514}
1515
1516static void update_signal_strength(int32_t signal_bars)
1517{
1518 if (signal_bars < 0) {
1519 DBG("signal strength smaller than expected: %d < 0",
1520 signal_bars);
1521 signal_bars = 0;
1522 } else if (signal_bars > 5) {
1523 DBG("signal strength greater than expected: %d > 5",
1524 signal_bars);
1525 signal_bars = 5;
1526 }
1527
1528 if (net.signal_bars == signal_bars)
1529 return;
1530
1531 telephony_update_indicator(maemo_indicators, "signal", signal_bars);
1532
1533 net.signal_bars = signal_bars;
1534 DBG("telephony-maemo6: signal strength updated: %d/5", signal_bars);
1535}
1536
1537static void handle_signal_bars_changed(DBusMessage *msg)
1538{
1539 int32_t signal_bars;
1540
1541 if (!dbus_message_get_args(msg, NULL,
1542 DBUS_TYPE_INT32, &signal_bars,
1543 DBUS_TYPE_INVALID)) {
1544 error("Unexpected parameters in SignalBarsChanged");
1545 return;
1546 }
1547
1548 update_signal_strength(signal_bars);
1549}
1550
1551static gboolean iter_get_basic_args(DBusMessageIter *iter,
1552 int first_arg_type, ...)
1553{
1554 int type;
1555 va_list ap;
1556
1557 va_start(ap, first_arg_type);
1558
1559 for (type = first_arg_type; type != DBUS_TYPE_INVALID;
1560 type = va_arg(ap, int)) {
1561 void *value = va_arg(ap, void *);
1562 int real_type = dbus_message_iter_get_arg_type(iter);
1563
1564 if (real_type != type) {
1565 error("iter_get_basic_args: expected %c but got %c",
1566 (char) type, (char) real_type);
1567 break;
1568 }
1569
1570 dbus_message_iter_get_basic(iter, value);
1571 dbus_message_iter_next(iter);
1572 }
1573
1574 va_end(ap);
1575
1576 return type == DBUS_TYPE_INVALID ? TRUE : FALSE;
1577}
1578
1579static void hal_battery_level_reply(DBusPendingCall *call, void *user_data)
1580{
1581 DBusError err;
1582 DBusMessage *reply;
1583 dbus_int32_t level;
1584 int *value = user_data;
1585
1586 reply = dbus_pending_call_steal_reply(call);
1587
1588 dbus_error_init(&err);
1589 if (dbus_set_error_from_message(&err, reply)) {
1590 error("hald replied with an error: %s, %s",
1591 err.name, err.message);
1592 dbus_error_free(&err);
1593 goto done;
1594 }
1595
1596 if (!dbus_message_get_args(reply, NULL,
1597 DBUS_TYPE_INT32, &level,
1598 DBUS_TYPE_INVALID)) {
1599 error("Unexpected args in hald reply");
1600 goto done;
1601 }
1602
1603 *value = (int) level;
1604
1605 if (value == &battchg_last)
1606 DBG("telephony-maemo6: battery.charge_level.last_full is %d",
1607 *value);
1608 else if (value == &battchg_design)
1609 DBG("telephony-maemo6: battery.charge_level.design is %d",
1610 *value);
1611 else
1612 DBG("telephony-maemo6: battery.charge_level.current is %d",
1613 *value);
1614
1615 if ((battchg_design > 0 || battchg_last > 0) && battchg_cur >= 0) {
1616 int new, max;
1617
1618 if (battchg_last > 0)
1619 max = battchg_last;
1620 else
1621 max = battchg_design;
1622
1623 new = battchg_cur * 5 / max;
1624
1625 telephony_update_indicator(maemo_indicators, "battchg", new);
1626 }
1627
1628done:
1629 dbus_message_unref(reply);
1630 remove_pending(call);
1631}
1632
1633static void hal_get_integer(const char *path, const char *key, void *user_data)
1634{
1635 send_method_call("org.freedesktop.Hal", path,
1636 "org.freedesktop.Hal.Device",
1637 "GetPropertyInteger",
1638 hal_battery_level_reply, user_data,
1639 DBUS_TYPE_STRING, &key,
1640 DBUS_TYPE_INVALID);
1641}
1642
1643static void handle_hal_property_modified(DBusMessage *msg)
1644{
1645 DBusMessageIter iter, array;
1646 dbus_int32_t num_changes;
1647 const char *path;
1648
1649 path = dbus_message_get_path(msg);
1650
1651 dbus_message_iter_init(msg, &iter);
1652
1653 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
1654 error("Unexpected signature in hal PropertyModified signal");
1655 return;
1656 }
1657
1658 dbus_message_iter_get_basic(&iter, &num_changes);
1659 dbus_message_iter_next(&iter);
1660
1661 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1662 error("Unexpected signature in hal PropertyModified signal");
1663 return;
1664 }
1665
1666 dbus_message_iter_recurse(&iter, &array);
1667
1668 while (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_INVALID) {
1669 DBusMessageIter prop;
1670 const char *name;
1671 dbus_bool_t added, removed;
1672
1673 dbus_message_iter_recurse(&array, &prop);
1674
1675 if (!iter_get_basic_args(&prop,
1676 DBUS_TYPE_STRING, &name,
1677 DBUS_TYPE_BOOLEAN, &added,
1678 DBUS_TYPE_BOOLEAN, &removed,
1679 DBUS_TYPE_INVALID)) {
1680 error("Invalid hal PropertyModified parameters");
1681 break;
1682 }
1683
1684 if (g_str_equal(name, "battery.charge_level.last_full"))
1685 hal_get_integer(path, name, &battchg_last);
1686 else if (g_str_equal(name, "battery.charge_level.current"))
1687 hal_get_integer(path, name, &battchg_cur);
1688 else if (g_str_equal(name, "battery.charge_level.design"))
1689 hal_get_integer(path, name, &battchg_design);
1690
1691 dbus_message_iter_next(&array);
1692 }
1693}
1694
1695static void csd_call_free(void *data)
1696{
1697 struct csd_call *call = data;
1698
1699 if (!call)
1700 return;
1701
1702 g_free(call->object_path);
1703 g_free(call->number);
1704
1705 g_slist_foreach(pending, remove_pending_by_data, call);
1706
1707 g_free(call);
1708}
1709
1710static void parse_call_list(DBusMessageIter *iter)
1711{
1712 do {
1713 DBusMessageIter call_iter;
1714 struct csd_call *call;
1715 const char *object_path, *number;
1716 dbus_uint32_t status;
1717 dbus_bool_t originating, terminating, emerg, on_hold, conf;
1718
1719 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRUCT) {
1720 error("Unexpected signature in GetCallInfoAll reply");
1721 break;
1722 }
1723
1724 dbus_message_iter_recurse(iter, &call_iter);
1725
1726 if (!iter_get_basic_args(&call_iter,
1727 DBUS_TYPE_OBJECT_PATH, &object_path,
1728 DBUS_TYPE_UINT32, &status,
1729 DBUS_TYPE_BOOLEAN, &originating,
1730 DBUS_TYPE_BOOLEAN, &terminating,
1731 DBUS_TYPE_BOOLEAN, &emerg,
1732 DBUS_TYPE_BOOLEAN, &on_hold,
1733 DBUS_TYPE_BOOLEAN, &conf,
1734 DBUS_TYPE_STRING, &number,
1735 DBUS_TYPE_INVALID)) {
1736 error("Parsing call D-Bus parameters failed");
1737 break;
1738 }
1739
1740 call = find_call(object_path);
1741 if (!call) {
1742 call = g_new0(struct csd_call, 1);
1743 call->object_path = g_strdup(object_path);
1744 calls = g_slist_append(calls, call);
1745 DBG("telephony-maemo6: new csd call instance at %s",
1746 object_path);
1747 }
1748
1749 if (status == CSD_CALL_STATUS_IDLE)
1750 continue;
1751
1752 /* CSD gives incorrect call_hold property sometimes */
1753 if ((call->status != CSD_CALL_STATUS_HOLD && on_hold) ||
1754 (call->status == CSD_CALL_STATUS_HOLD &&
1755 !on_hold)) {
1756 error("Conflicting call status and on_hold property!");
1757 on_hold = call->status == CSD_CALL_STATUS_HOLD;
1758 }
1759
1760 call->originating = originating;
1761 call->on_hold = on_hold;
1762 call->conference = conf;
1763 g_free(call->number);
1764 call->number = g_strdup(number);
1765
1766 /* Update indicators */
1767 call_set_status(call, status);
1768
1769 } while (dbus_message_iter_next(iter));
1770}
1771
1772static void update_operator_name(const char *name)
1773{
1774 if (name == NULL)
1775 return;
1776
1777 g_free(net.operator_name);
1778 net.operator_name = g_strndup(name, 16);
1779 DBG("telephony-maemo6: operator name updated: %s", name);
1780}
1781
1782static void get_property_reply(DBusPendingCall *call, void *user_data)
1783{
1784 char *prop = user_data;
1785 DBusError err;
1786 DBusMessage *reply;
1787 DBusMessageIter iter, sub;
1788
1789 reply = dbus_pending_call_steal_reply(call);
1790
1791 dbus_error_init(&err);
1792 if (dbus_set_error_from_message(&err, reply)) {
1793 error("csd replied with an error: %s, %s",
1794 err.name, err.message);
1795 dbus_error_free(&err);
1796 goto done;
1797 }
1798
1799 dbus_message_iter_init(reply, &iter);
1800
1801 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1802 error("Unexpected signature in Get return");
1803 goto done;
1804 }
1805
1806 dbus_message_iter_recurse(&iter, &sub);
1807
1808 if (g_strcmp0(prop, "RegistrationStatus") == 0) {
1809 const char *status;
1810
1811 dbus_message_iter_get_basic(&sub, &status);
1812 update_registration_status(status);
1813
1814 get_property(CSD_CSNET_OPERATOR, "OperatorName");
1815 get_property(CSD_CSNET_SIGNAL, "SignalBars");
1816 } else if (g_strcmp0(prop, "OperatorName") == 0) {
1817 const char *name;
1818
1819 dbus_message_iter_get_basic(&sub, &name);
1820 update_operator_name(name);
1821 } else if (g_strcmp0(prop, "SignalBars") == 0) {
1822 int32_t signal_bars;
1823
1824 dbus_message_iter_get_basic(&sub, &signal_bars);
1825 update_signal_strength(signal_bars);
1826 }
1827
1828done:
1829 g_free(prop);
1830 dbus_message_unref(reply);
1831 remove_pending(call);
1832}
1833
1834static int get_property(const char *iface, const char *prop)
1835{
1836 return send_method_call(CSD_CSNET_BUS_NAME, CSD_CSNET_PATH,
1837 DBUS_INTERFACE_PROPERTIES, "Get",
1838 get_property_reply, g_strdup(prop),
1839 DBUS_TYPE_STRING, &iface,
1840 DBUS_TYPE_STRING, &prop,
1841 DBUS_TYPE_INVALID);
1842}
1843
1844static void handle_operator_name_changed(DBusMessage *msg)
1845{
1846 const char *name;
1847
1848 if (!dbus_message_get_args(msg, NULL,
1849 DBUS_TYPE_STRING, &name,
1850 DBUS_TYPE_INVALID)) {
1851 error("Unexpected parameters in OperatorNameChanged");
1852 return;
1853 }
1854
1855 update_operator_name(name);
1856}
1857
1858static void call_info_reply(DBusPendingCall *call, void *user_data)
1859{
1860 DBusError err;
1861 DBusMessage *reply;
1862 DBusMessageIter iter, sub;
1863
1864 get_calls_active = FALSE;
1865
1866 reply = dbus_pending_call_steal_reply(call);
1867
1868 dbus_error_init(&err);
1869 if (dbus_set_error_from_message(&err, reply)) {
1870 error("csd replied with an error: %s, %s",
1871 err.name, err.message);
1872 dbus_error_free(&err);
1873 goto done;
1874 }
1875
1876 dbus_message_iter_init(reply, &iter);
1877
1878 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
1879 error("Unexpected signature in GetCallInfoAll return");
1880 goto done;
1881 }
1882
1883 dbus_message_iter_recurse(&iter, &sub);
1884
1885 parse_call_list(&sub);
1886
1887 get_property(CSD_CSNET_REGISTRATION, "RegistrationStatus");
1888
1889done:
1890 dbus_message_unref(reply);
1891 remove_pending(call);
1892}
1893
1894
1895static void phonebook_read_reply(DBusPendingCall *call, void *user_data)
1896{
1897 DBusError derr;
1898 DBusMessage *reply;
1899 const char *name, *number, *secondname, *additionalnumber, *email;
1900 int index;
1901 char **number_type = user_data;
1902
1903 reply = dbus_pending_call_steal_reply(call);
1904
1905 dbus_error_init(&derr);
1906 if (dbus_set_error_from_message(&derr, reply)) {
1907 error("%s.ReadFirst replied with an error: %s, %s",
1908 CSD_SIMPB_INTERFACE, derr.name, derr.message);
1909 dbus_error_free(&derr);
1910 if (number_type == &vmbx)
1911 vmbx = g_strdup(getenv("VMBX_NUMBER"));
1912 goto done;
1913 }
1914
1915 dbus_error_init(&derr);
1916 if (dbus_message_get_args(reply, NULL,
1917 DBUS_TYPE_INT32, &index,
1918 DBUS_TYPE_STRING, &name,
1919 DBUS_TYPE_STRING, &number,
1920 DBUS_TYPE_STRING, &secondname,
1921 DBUS_TYPE_STRING, &additionalnumber,
1922 DBUS_TYPE_STRING, &email,
1923 DBUS_TYPE_INVALID) == FALSE) {
1924 error("Unable to parse %s.ReadFirst arguments: %s, %s",
1925 CSD_SIMPB_INTERFACE, derr.name, derr.message);
1926 dbus_error_free(&derr);
1927 goto done;
1928 }
1929
1930 if (number_type == &msisdn) {
1931 g_free(msisdn);
1932 msisdn = g_strdup(number);
1933 DBG("Got MSISDN %s (%s)", number, name);
1934 } else {
1935 g_free(vmbx);
1936 vmbx = g_strdup(number);
1937 DBG("Got voice mailbox number %s (%s)", number, name);
1938 }
1939
1940done:
1941 dbus_message_unref(reply);
1942 remove_pending(call);
1943}
1944
1945static void csd_init(void)
1946{
1947 const char *pb_type;
1948 int ret;
1949
1950 ret = send_method_call(CSD_CALL_BUS_NAME, CSD_CALL_PATH,
1951 CSD_CALL_INTERFACE, "GetCallInfoAll",
1952 call_info_reply, NULL, DBUS_TYPE_INVALID);
1953 if (ret < 0) {
1954 error("Unable to sent GetCallInfoAll method call");
1955 return;
1956 }
1957
1958 get_calls_active = TRUE;
1959
1960 pb_type = CSD_SIMPB_TYPE_MSISDN;
1961
1962 ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1963 CSD_SIMPB_INTERFACE, "ReadFirst",
1964 phonebook_read_reply, &msisdn,
1965 DBUS_TYPE_STRING, &pb_type,
1966 DBUS_TYPE_INVALID);
1967 if (ret < 0) {
1968 error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1969 return;
1970 }
1971
1972 /* Voicemail should be in MBDN index 0 */
1973 pb_type = CSD_SIMPB_TYPE_MBDN;
1974
1975 ret = send_method_call(CSD_SIMPB_BUS_NAME, CSD_SIMPB_PATH,
1976 CSD_SIMPB_INTERFACE, "ReadFirst",
1977 phonebook_read_reply, &vmbx,
1978 DBUS_TYPE_STRING, &pb_type,
1979 DBUS_TYPE_INVALID);
1980 if (ret < 0) {
1981 error("Unable to send " CSD_SIMPB_INTERFACE ".read()");
1982 return;
1983 }
1984}
1985
1986static void handle_modem_state(DBusMessage *msg)
1987{
1988 const char *state;
1989
1990 if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &state,
1991 DBUS_TYPE_INVALID)) {
1992 error("Unexpected modem state parameters");
1993 return;
1994 }
1995
1996 DBG("SSC modem state: %s", state);
1997
1998 if (calls != NULL || get_calls_active)
1999 return;
2000
2001 if (g_str_equal(state, "cmt_ready") || g_str_equal(state, "online"))
2002 csd_init();
2003}
2004
2005static void modem_state_reply(DBusPendingCall *call, void *user_data)
2006{
2007 DBusMessage *reply = dbus_pending_call_steal_reply(call);
2008 DBusError err;
2009
2010 dbus_error_init(&err);
2011 if (dbus_set_error_from_message(&err, reply)) {
2012 error("get_modem_state: %s, %s", err.name, err.message);
2013 dbus_error_free(&err);
2014 } else
2015 handle_modem_state(reply);
2016
2017 dbus_message_unref(reply);
2018 remove_pending(call);
2019}
2020
2021static gboolean signal_filter(DBusConnection *conn, DBusMessage *msg,
2022 void *data)
2023{
2024 const char *path = dbus_message_get_path(msg);
2025
2026 if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Coming"))
2027 handle_incoming_call(msg);
2028 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE, "Created"))
2029 handle_outgoing_call(msg);
2030 else if (dbus_message_is_signal(msg, CSD_CALL_INTERFACE,
2031 "CreateRequested"))
2032 handle_create_requested(msg);
2033 else if (dbus_message_is_signal(msg, CSD_CALL_INSTANCE, "CallStatus"))
2034 handle_call_status(msg, path);
2035 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Joined"))
2036 handle_conference(msg, TRUE);
2037 else if (dbus_message_is_signal(msg, CSD_CALL_CONFERENCE, "Left"))
2038 handle_conference(msg, FALSE);
2039 else if (dbus_message_is_signal(msg, CSD_CSNET_REGISTRATION,
2040 "RegistrationChanged"))
2041 handle_registration_changed(msg);
2042 else if (dbus_message_is_signal(msg, CSD_CSNET_OPERATOR,
2043 "OperatorNameChanged"))
2044 handle_operator_name_changed(msg);
2045 else if (dbus_message_is_signal(msg, CSD_CSNET_SIGNAL,
2046 "SignalBarsChanged"))
2047 handle_signal_bars_changed(msg);
2048 else if (dbus_message_is_signal(msg, "org.freedesktop.Hal.Device",
2049 "PropertyModified"))
2050 handle_hal_property_modified(msg);
2051 else if (dbus_message_is_signal(msg, SSC_DBUS_IFACE,
2052 "modem_state_changed_ind"))
2053 handle_modem_state(msg);
2054
2055 return TRUE;
2056}
2057
2058static void add_watch(const char *sender, const char *path,
2059 const char *interface, const char *member)
2060{
2061 guint watch;
2062
2063 watch = g_dbus_add_signal_watch(connection, sender, path, interface,
2064 member, signal_filter, NULL, NULL);
2065
2066 watches = g_slist_prepend(watches, GUINT_TO_POINTER(watch));
2067}
2068
2069static void hal_find_device_reply(DBusPendingCall *call, void *user_data)
2070{
2071 DBusError err;
2072 DBusMessage *reply;
2073 DBusMessageIter iter, sub;
2074 const char *path;
2075 int type;
2076
2077 reply = dbus_pending_call_steal_reply(call);
2078
2079 dbus_error_init(&err);
2080 if (dbus_set_error_from_message(&err, reply)) {
2081 error("hald replied with an error: %s, %s",
2082 err.name, err.message);
2083 dbus_error_free(&err);
2084 goto done;
2085 }
2086
2087 dbus_message_iter_init(reply, &iter);
2088
2089 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
2090 error("Unexpected signature in FindDeviceByCapability return");
2091 goto done;
2092 }
2093
2094 dbus_message_iter_recurse(&iter, &sub);
2095
2096 type = dbus_message_iter_get_arg_type(&sub);
2097
2098 if (type != DBUS_TYPE_OBJECT_PATH && type != DBUS_TYPE_STRING) {
2099 error("No hal device with battery capability found");
2100 goto done;
2101 }
2102
2103 dbus_message_iter_get_basic(&sub, &path);
2104
2105 DBG("telephony-maemo6: found battery device at %s", path);
2106
2107 add_watch(NULL, path, "org.freedesktop.Hal.Device",
2108 "PropertyModified");
2109
2110 hal_get_integer(path, "battery.charge_level.last_full", &battchg_last);
2111 hal_get_integer(path, "battery.charge_level.current", &battchg_cur);
2112 hal_get_integer(path, "battery.charge_level.design", &battchg_design);
2113
2114done:
2115 dbus_message_unref(reply);
2116 remove_pending(call);
2117}
2118
2119int telephony_init(void)
2120{
2121 const char *battery_cap = "battery";
2122 uint32_t features = AG_FEATURE_EC_ANDOR_NR |
2123 AG_FEATURE_INBAND_RINGTONE |
2124 AG_FEATURE_REJECT_A_CALL |
2125 AG_FEATURE_ENHANCED_CALL_STATUS |
2126 AG_FEATURE_ENHANCED_CALL_CONTROL |
2127 AG_FEATURE_EXTENDED_ERROR_RESULT_CODES |
2128 AG_FEATURE_THREE_WAY_CALLING;
2129 int i;
2130
2131 DBG("");
2132
2133 connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
2134
2135 add_watch(NULL, NULL, CSD_CALL_INTERFACE, NULL);
2136 add_watch(NULL, NULL, CSD_CALL_INSTANCE, NULL);
2137 add_watch(NULL, NULL, CSD_CALL_CONFERENCE, NULL);
2138 add_watch(NULL, NULL, CSD_CSNET_REGISTRATION, "RegistrationChanged");
2139 add_watch(NULL, NULL, CSD_CSNET_OPERATOR, "OperatorNameChanged");
2140 add_watch(NULL, NULL, CSD_CSNET_SIGNAL, "SignalBarsChanged");
2141 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: