hfp_hf: Fix up whitespace corruption
[platform/upstream/ofono.git] / plugins / hfp_hf.c
1 /*
2  *
3  *  oFono - Open Source Telephony
4  *
5  *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2010  ProFUSION embedded systems
7  *  Copyright (C) 2011  BMW Car IT GmbH. All rights reserved.
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License version 2 as
11  *  published by the Free Software Foundation.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
21  *
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include <errno.h>
31 #include <unistd.h>
32 #include <glib.h>
33 #include <gatchat.h>
34 #include <gattty.h>
35 #include <gdbus.h>
36 #include <ofono.h>
37
38 #define OFONO_API_SUBJECT_TO_CHANGE
39 #include <ofono/plugin.h>
40 #include <ofono/log.h>
41 #include <ofono/modem.h>
42 #include <ofono/devinfo.h>
43 #include <ofono/netreg.h>
44 #include <ofono/voicecall.h>
45 #include <ofono/call-volume.h>
46 #include <ofono/handsfree.h>
47
48 #include <drivers/hfpmodem/slc.h>
49
50 #include "bluetooth.h"
51
52 #define BLUEZ_GATEWAY_INTERFACE         BLUEZ_SERVICE ".HandsfreeGateway"
53
54 #define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent"
55 #define HFP_AGENT_ERROR_INTERFACE "org.bluez.Error"
56
57 #ifndef DBUS_TYPE_UNIX_FD
58 #define DBUS_TYPE_UNIX_FD -1
59 #endif
60
61 static DBusConnection *connection;
62 static GHashTable *modem_hash = NULL;
63
64 struct hfp_data {
65         struct hfp_slc_info info;
66         char *handsfree_path;
67         char *handsfree_address;
68         DBusMessage *slc_msg;
69         gboolean agent_registered;
70         DBusPendingCall *call;
71 };
72
73 static void hfp_debug(const char *str, void *user_data)
74 {
75         const char *prefix = user_data;
76
77         ofono_info("%s%s", prefix, str);
78 }
79
80 static void slc_established(gpointer userdata)
81 {
82         struct ofono_modem *modem = userdata;
83         struct hfp_data *data = ofono_modem_get_data(modem);
84         DBusMessage *msg;
85
86         ofono_modem_set_powered(modem, TRUE);
87
88         msg = dbus_message_new_method_return(data->slc_msg);
89         g_dbus_send_message(connection, msg);
90         dbus_message_unref(data->slc_msg);
91         data->slc_msg = NULL;
92
93         ofono_info("Service level connection established");
94 }
95
96 static void slc_failed(gpointer userdata)
97 {
98         struct ofono_modem *modem = userdata;
99         struct hfp_data *data = ofono_modem_get_data(modem);
100         DBusMessage *msg;
101
102         msg = g_dbus_create_error(data->slc_msg, HFP_AGENT_ERROR_INTERFACE
103                                         ".Failed",
104                                         "HFP Handshake failed");
105         g_dbus_send_message(connection, msg);
106         dbus_message_unref(data->slc_msg);
107         data->slc_msg = NULL;
108
109         ofono_error("Service level connection failed");
110         ofono_modem_set_powered(modem, FALSE);
111
112         g_at_chat_unref(data->info.chat);
113         data->info.chat = NULL;
114 }
115
116 static void hfp_disconnected_cb(gpointer user_data)
117 {
118         struct ofono_modem *modem = user_data;
119         struct hfp_data *data = ofono_modem_get_data(modem);
120
121         ofono_modem_set_powered(modem, FALSE);
122
123         g_at_chat_unref(data->info.chat);
124         data->info.chat = NULL;
125 }
126
127 /* either oFono or Phone could request SLC connection */
128 static int service_level_connection(struct ofono_modem *modem, int fd)
129 {
130         struct hfp_data *data = ofono_modem_get_data(modem);
131         GIOChannel *io;
132         GAtSyntax *syntax;
133         GAtChat *chat;
134
135         io = g_io_channel_unix_new(fd);
136         if (io == NULL) {
137                 ofono_error("Service level connection failed: %s (%d)",
138                         strerror(errno), errno);
139                 return -EIO;
140         }
141
142         syntax = g_at_syntax_new_gsm_permissive();
143         chat = g_at_chat_new(io, syntax);
144         g_at_syntax_unref(syntax);
145         g_io_channel_unref(io);
146
147         if (chat == NULL)
148                 return -ENOMEM;
149
150         g_at_chat_set_disconnect_function(chat, hfp_disconnected_cb, modem);
151
152         if (getenv("OFONO_AT_DEBUG"))
153                 g_at_chat_set_debug(chat, hfp_debug, "");
154
155         data->info.chat = chat;
156         hfp_slc_establish(&data->info, slc_established, slc_failed, modem);
157
158         return -EINPROGRESS;
159 }
160
161 static DBusMessage *hfp_agent_new_connection(DBusConnection *conn,
162                                                 DBusMessage *msg, void *data)
163 {
164         int fd, err;
165         struct ofono_modem *modem = data;
166         struct hfp_data *hfp_data = ofono_modem_get_data(modem);
167         guint16 version;
168
169         if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd,
170                                 DBUS_TYPE_UINT16, &version, DBUS_TYPE_INVALID))
171                 return __ofono_error_invalid_args(msg);
172
173         hfp_slc_info_init(&hfp_data->info, version);
174
175         err = service_level_connection(modem, fd);
176         if (err < 0 && err != -EINPROGRESS)
177                 return __ofono_error_failed(msg);
178
179         hfp_data->slc_msg = msg;
180         dbus_message_ref(msg);
181
182         return NULL;
183 }
184
185 static DBusMessage *hfp_agent_release(DBusConnection *conn,
186                                         DBusMessage *msg, void *data)
187 {
188         struct ofono_modem *modem = data;
189         struct hfp_data *hfp_data = ofono_modem_get_data(modem);
190         const char *obj_path = ofono_modem_get_path(modem);
191
192         g_dbus_unregister_interface(connection, obj_path, HFP_AGENT_INTERFACE);
193         hfp_data->agent_registered = FALSE;
194
195         g_hash_table_remove(modem_hash, hfp_data->handsfree_path);
196         ofono_modem_remove(modem);
197
198         return dbus_message_new_method_return(msg);
199 }
200
201 static const GDBusMethodTable agent_methods[] = {
202         { GDBUS_ASYNC_METHOD("NewConnection",
203                                 GDBUS_ARGS({ "fd", "h" }, { "version", "q" }),
204                                 NULL, hfp_agent_new_connection) },
205         { GDBUS_METHOD("Release", NULL, NULL, hfp_agent_release) },
206         { }
207 };
208
209 static int hfp_hf_probe(const char *device, const char *dev_addr,
210                                 const char *adapter_addr, const char *alias)
211 {
212         struct ofono_modem *modem;
213         struct hfp_data *data;
214         char buf[256];
215
216         /* We already have this device in our hash, ignore */
217         if (g_hash_table_lookup(modem_hash, device) != NULL)
218                 return -EALREADY;
219
220         ofono_info("Using device: %s, devaddr: %s, adapter: %s",
221                         device, dev_addr, adapter_addr);
222
223         strcpy(buf, "hfp/");
224         bluetooth_create_path(dev_addr, adapter_addr, buf + 4, sizeof(buf) - 4);
225
226         modem = ofono_modem_create(buf, "hfp");
227         if (modem == NULL)
228                 return -ENOMEM;
229
230         data = g_try_new0(struct hfp_data, 1);
231         if (data == NULL)
232                 goto free;
233
234         data->handsfree_path = g_strdup(device);
235         if (data->handsfree_path == NULL)
236                 goto free;
237
238         data->handsfree_address = g_strdup(dev_addr);
239         if (data->handsfree_address == NULL)
240                 goto free;
241
242         ofono_modem_set_data(modem, data);
243         ofono_modem_set_name(modem, alias);
244         ofono_modem_register(modem);
245
246         g_hash_table_insert(modem_hash, g_strdup(device), modem);
247
248         return 0;
249
250 free:
251         if (data != NULL)
252                 g_free(data->handsfree_path);
253
254         g_free(data);
255         ofono_modem_remove(modem);
256
257         return -ENOMEM;
258 }
259
260 static gboolean hfp_remove_modem(gpointer key, gpointer value,
261                                         gpointer user_data)
262 {
263         struct ofono_modem *modem = value;
264         const char *device = key;
265         const char *prefix = user_data;
266
267         if (prefix && g_str_has_prefix(device, prefix) == FALSE)
268                 return FALSE;
269
270         ofono_modem_remove(modem);
271
272         return TRUE;
273 }
274
275 static void hfp_hf_remove(const char *prefix)
276 {
277         DBG("%s", prefix);
278
279         if (modem_hash == NULL)
280                 return;
281
282         g_hash_table_foreach_remove(modem_hash, hfp_remove_modem,
283                                                         (gpointer) prefix);
284 }
285
286 static void hfp_hf_set_alias(const char *device, const char *alias)
287 {
288         struct ofono_modem *modem;
289
290         if (device == NULL || alias == NULL)
291                 return;
292
293         modem = g_hash_table_lookup(modem_hash, device);
294         if (modem == NULL)
295                 return;
296
297         ofono_modem_set_name(modem, alias);
298 }
299
300 static int hfp_register_ofono_handsfree(struct ofono_modem *modem)
301 {
302         const char *obj_path = ofono_modem_get_path(modem);
303         struct hfp_data *data = ofono_modem_get_data(modem);
304         DBusMessage *msg;
305
306         DBG("Registering oFono Agent to bluetooth daemon");
307
308         msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path,
309                                 BLUEZ_GATEWAY_INTERFACE, "RegisterAgent");
310         if (msg == NULL)
311                 return -ENOMEM;
312
313         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path,
314                                 DBUS_TYPE_INVALID);
315
316         g_dbus_send_message(connection, msg);
317         return 0;
318 }
319
320 static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem)
321 {
322         const char *obj_path = ofono_modem_get_path(modem);
323         struct hfp_data *data = ofono_modem_get_data(modem);
324         DBusMessage *msg;
325
326         DBG("Unregistering oFono Agent from bluetooth daemon");
327
328         msg = dbus_message_new_method_call(BLUEZ_SERVICE, data->handsfree_path,
329                                 BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent");
330         if (msg == NULL)
331                 return -ENOMEM;
332
333         dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &obj_path,
334                                 DBUS_TYPE_INVALID);
335
336         g_dbus_send_message(connection, msg);
337         return 0;
338 }
339
340 static int hfp_probe(struct ofono_modem *modem)
341 {
342         const char *obj_path = ofono_modem_get_path(modem);
343         struct hfp_data *data = ofono_modem_get_data(modem);
344
345         if (data == NULL)
346                 return -EINVAL;
347
348         g_dbus_register_interface(connection, obj_path, HFP_AGENT_INTERFACE,
349                         agent_methods, NULL, NULL, modem, NULL);
350
351         data->agent_registered = TRUE;
352
353         if (hfp_register_ofono_handsfree(modem) != 0)
354                 return -EINVAL;
355
356         return 0;
357 }
358
359 static void hfp_remove(struct ofono_modem *modem)
360 {
361         struct hfp_data *data = ofono_modem_get_data(modem);
362         const char *obj_path = ofono_modem_get_path(modem);
363
364         if (data->call != NULL)
365                 dbus_pending_call_cancel(data->call);
366
367         if (g_dbus_unregister_interface(connection, obj_path,
368                                         HFP_AGENT_INTERFACE))
369                 hfp_unregister_ofono_handsfree(modem);
370
371         g_free(data->handsfree_address);
372         g_free(data->handsfree_path);
373         g_free(data);
374
375         ofono_modem_set_data(modem, NULL);
376 }
377
378 static void hfp_connect_reply(DBusPendingCall *call, gpointer user_data)
379 {
380         struct ofono_modem *modem = user_data;
381         struct hfp_data *data = ofono_modem_get_data(modem);
382         DBusError derr;
383         DBusMessage *reply, *msg;
384
385         reply = dbus_pending_call_steal_reply(call);
386
387         if (ofono_modem_get_powered(modem))
388                 goto done;
389
390         dbus_error_init(&derr);
391         if (!dbus_set_error_from_message(&derr, reply))
392                 goto done;
393
394         DBG("Connect reply: %s", derr.message);
395
396         if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) {
397                 msg = dbus_message_new_method_call(BLUEZ_SERVICE,
398                                 data->handsfree_path,
399                                 BLUEZ_GATEWAY_INTERFACE, "Disconnect");
400                 if (msg == NULL)
401                         ofono_error("Disconnect failed");
402                 else
403                         g_dbus_send_message(connection, msg);
404         }
405
406         ofono_modem_set_powered(modem, FALSE);
407
408         dbus_error_free(&derr);
409
410 done:
411         dbus_message_unref(reply);
412         data->call = NULL;
413 }
414
415 /* power up hardware */
416 static int hfp_enable(struct ofono_modem *modem)
417 {
418         struct hfp_data *data = ofono_modem_get_data(modem);
419         int status;
420
421         DBG("%p", modem);
422
423         status = bluetooth_send_with_reply(data->handsfree_path,
424                                         BLUEZ_GATEWAY_INTERFACE, "Connect",
425                                         &data->call, hfp_connect_reply,
426                                         modem, NULL,
427                                         DBUS_TIMEOUT, DBUS_TYPE_INVALID);
428
429         if (status < 0)
430                 return -EINVAL;
431
432         return -EINPROGRESS;
433 }
434
435 static void hfp_power_down(DBusPendingCall *call, gpointer user_data)
436 {
437         struct ofono_modem *modem = user_data;
438         struct hfp_data *data = ofono_modem_get_data(modem);
439         DBusMessage *reply;
440         DBusError derr;
441
442         reply = dbus_pending_call_steal_reply(call);
443
444         dbus_error_init(&derr);
445         if (dbus_set_error_from_message(&derr, reply)) {
446                 DBG("Disconnect reply: %s", derr.message);
447                 dbus_error_free(&derr);
448                 goto done;
449         }
450
451         ofono_modem_set_powered(modem, FALSE);
452
453 done:
454         dbus_message_unref(reply);
455         data->call = NULL;
456 }
457
458 static int hfp_disable(struct ofono_modem *modem)
459 {
460         struct hfp_data *data = ofono_modem_get_data(modem);
461         int status;
462
463         DBG("%p", modem);
464
465         g_at_chat_unref(data->info.chat);
466         data->info.chat = NULL;
467
468         if (data->agent_registered) {
469                 status = bluetooth_send_with_reply(data->handsfree_path,
470                                         BLUEZ_GATEWAY_INTERFACE, "Disconnect",
471                                         &data->call, hfp_power_down,
472                                         modem, NULL,
473                                         DBUS_TIMEOUT, DBUS_TYPE_INVALID);
474
475                 if (status < 0)
476                         return -EINVAL;
477         }
478
479         return -EINPROGRESS;
480 }
481
482 static void hfp_pre_sim(struct ofono_modem *modem)
483 {
484         struct hfp_data *data = ofono_modem_get_data(modem);
485
486         DBG("%p", modem);
487
488         ofono_devinfo_create(modem, 0, "hfpmodem", data->handsfree_address);
489         ofono_voicecall_create(modem, 0, "hfpmodem", &data->info);
490         ofono_netreg_create(modem, 0, "hfpmodem", &data->info);
491         ofono_call_volume_create(modem, 0, "hfpmodem", &data->info);
492         ofono_handsfree_create(modem, 0, "hfpmodem", &data->info);
493 }
494
495 static void hfp_post_sim(struct ofono_modem *modem)
496 {
497         DBG("%p", modem);
498 }
499
500 static struct ofono_modem_driver hfp_driver = {
501         .name           = "hfp",
502         .modem_type     = OFONO_MODEM_TYPE_HFP,
503         .probe          = hfp_probe,
504         .remove         = hfp_remove,
505         .enable         = hfp_enable,
506         .disable        = hfp_disable,
507         .pre_sim        = hfp_pre_sim,
508         .post_sim       = hfp_post_sim,
509 };
510
511 static struct bluetooth_profile hfp_hf = {
512         .name           = "hfp_hf",
513         .probe          = hfp_hf_probe,
514         .remove         = hfp_hf_remove,
515         .set_alias      = hfp_hf_set_alias,
516 };
517
518 static int hfp_init(void)
519 {
520         int err;
521
522         if (DBUS_TYPE_UNIX_FD < 0)
523                 return -EBADF;
524
525         connection = ofono_dbus_get_connection();
526
527         err = ofono_modem_driver_register(&hfp_driver);
528         if (err < 0)
529                 return err;
530
531         err = bluetooth_register_uuid(HFP_AG_UUID, &hfp_hf);
532         if (err < 0) {
533                 ofono_modem_driver_unregister(&hfp_driver);
534                 return err;
535         }
536
537         modem_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
538                                                 g_free, NULL);
539
540         return 0;
541 }
542
543 static void hfp_exit(void)
544 {
545         bluetooth_unregister_uuid(HFP_AG_UUID);
546         ofono_modem_driver_unregister(&hfp_driver);
547
548         g_hash_table_destroy(modem_hash);
549 }
550
551 OFONO_PLUGIN_DEFINE(hfp, "Hands-Free Profile Plugins", VERSION,
552                         OFONO_PLUGIN_PRIORITY_DEFAULT, hfp_init, hfp_exit)