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