2 * oFono - Open Source Telephony
4 * Copyright (C) 2011 Intel Corporation. All rights reserved.
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
29 #include <sys/socket.h>
34 #define OFONO_API_SUBJECT_TO_CHANGE
35 #include <ofono/plugin.h>
36 #include <ofono/log.h>
37 #include <ofono/modem.h>
42 #ifndef DBUS_TYPE_UNIX_FD
43 #define DBUS_TYPE_UNIX_FD -1
46 #define HFP_AG_EXT_PROFILE_PATH "/bluetooth/profile/hfp_ag"
48 static guint modemwatch_id;
50 static GHashTable *sim_hash = NULL;
51 static GHashTable *connection_hash;
53 static void connection_destroy(gpointer data)
55 int fd = GPOINTER_TO_INT(data);
62 static gboolean io_hup_cb(GIOChannel *io, GIOCondition cond, gpointer data)
66 DBG("Remove %s", device);
68 g_hash_table_remove(connection_hash, device);
73 static DBusMessage *profile_new_connection(DBusConnection *conn,
74 DBusMessage *msg, void *data)
76 DBusMessageIter entry;
80 struct ofono_emulator *em;
81 struct ofono_modem *modem;
83 DBG("Profile handler NewConnection");
85 if (dbus_message_iter_init(msg, &entry) == FALSE)
88 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_OBJECT_PATH)
91 dbus_message_iter_get_basic(&entry, &device);
92 dbus_message_iter_next(&entry);
94 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UNIX_FD)
97 dbus_message_iter_get_basic(&entry, &fd);
98 dbus_message_iter_next(&entry);
105 /* Pick the first voicecall capable modem */
106 if (modems == NULL) {
108 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
110 "No voice call capable modem");
113 modem = modems->data;
114 DBG("Picked modem %p for emulator", modem);
116 em = ofono_emulator_create(modem, OFONO_EMULATOR_TYPE_HFP);
119 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
121 "Not enough resources");
124 ofono_emulator_register(em, fd);
127 io = g_io_channel_unix_new(fd_dup);
128 g_io_add_watch_full(io, G_PRIORITY_DEFAULT, G_IO_HUP, io_hup_cb,
129 g_strdup(device), g_free);
130 g_io_channel_unref(io);
132 g_hash_table_insert(connection_hash, g_strdup(device),
133 GINT_TO_POINTER(fd_dup));
135 return dbus_message_new_method_return(msg);
138 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected",
139 "Invalid arguments in method call");
142 static DBusMessage *profile_release(DBusConnection *conn,
143 DBusMessage *msg, void *user_data)
145 DBG("Profile handler Release");
147 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
149 "Implementation not provided");
152 static DBusMessage *profile_cancel(DBusConnection *conn,
153 DBusMessage *msg, void *user_data)
155 DBG("Profile handler Cancel");
157 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
159 "Implementation not provided");
162 static DBusMessage *profile_disconnection(DBusConnection *conn,
163 DBusMessage *msg, void *user_data)
165 DBusMessageIter iter;
169 DBG("Profile handler RequestDisconnection");
171 if (!dbus_message_iter_init(msg, &iter))
174 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
177 dbus_message_iter_get_basic(&iter, &device);
181 fd = g_hash_table_lookup(connection_hash, device);
185 shutdown(GPOINTER_TO_INT(fd), SHUT_RDWR);
187 g_hash_table_remove(connection_hash, device);
189 return dbus_message_new_method_return(msg);
192 return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE ".Rejected",
193 "Invalid arguments in method call");
196 static const GDBusMethodTable profile_methods[] = {
197 { GDBUS_ASYNC_METHOD("NewConnection",
198 GDBUS_ARGS({ "device", "o"}, { "fd", "h"},
199 { "fd_properties", "a{sv}" }),
200 NULL, profile_new_connection) },
201 { GDBUS_METHOD("Release", NULL, NULL, profile_release) },
202 { GDBUS_METHOD("Cancel", NULL, NULL, profile_cancel) },
203 { GDBUS_METHOD("RequestDisconnection",
204 GDBUS_ARGS({"device", "o"}), NULL,
205 profile_disconnection) },
209 static void sim_state_watch(enum ofono_sim_state new_state, void *data)
211 struct ofono_modem *modem = data;
212 DBusConnection *conn = ofono_dbus_get_connection();
214 if (new_state != OFONO_SIM_STATE_READY) {
218 modems = g_list_remove(modems, modem);
222 bt_unregister_profile(conn, HFP_AG_EXT_PROFILE_PATH);
227 if (__ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_VOICECALL) == NULL)
230 modems = g_list_append(modems, modem);
232 if (modems->next != NULL)
235 bt_register_profile(conn, HFP_AG_UUID, HFP_VERSION_1_5, "hfp_ag",
236 HFP_AG_EXT_PROFILE_PATH, NULL, 0);
239 static gboolean sim_watch_remove(gpointer key, gpointer value,
242 struct ofono_sim *sim = key;
244 ofono_sim_remove_state_watch(sim, GPOINTER_TO_UINT(value));
249 static void sim_watch(struct ofono_atom *atom,
250 enum ofono_atom_watch_condition cond,
253 struct ofono_sim *sim = __ofono_atom_get_data(atom);
254 struct ofono_modem *modem = data;
257 if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
258 sim_state_watch(OFONO_SIM_STATE_NOT_PRESENT, modem);
260 sim_watch_remove(sim, g_hash_table_lookup(sim_hash, sim), NULL);
261 g_hash_table_remove(sim_hash, sim);
266 watch = ofono_sim_add_state_watch(sim, sim_state_watch, modem, NULL);
267 g_hash_table_insert(sim_hash, sim, GUINT_TO_POINTER(watch));
268 sim_state_watch(ofono_sim_get_state(sim), modem);
271 static void voicecall_watch(struct ofono_atom *atom,
272 enum ofono_atom_watch_condition cond,
275 struct ofono_atom *sim_atom;
276 struct ofono_sim *sim;
277 struct ofono_modem *modem;
278 DBusConnection *conn = ofono_dbus_get_connection();
280 if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED)
284 * This logic is only intended to handle voicecall atoms
285 * registered in post_sim state or later
287 modem = __ofono_atom_get_modem(atom);
289 sim_atom = __ofono_modem_find_atom(modem, OFONO_ATOM_TYPE_SIM);
290 if (sim_atom == NULL)
293 sim = __ofono_atom_get_data(sim_atom);
294 if (ofono_sim_get_state(sim) != OFONO_SIM_STATE_READY)
297 modems = g_list_append(modems, modem);
299 if (modems->next != NULL)
302 bt_register_profile(conn, HFP_AG_UUID, HFP_VERSION_1_5, "hfp_ag",
303 HFP_AG_EXT_PROFILE_PATH, NULL, 0);
306 static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
308 DBG("modem: %p, added: %d", modem, added);
313 __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_SIM,
314 sim_watch, modem, NULL);
315 __ofono_modem_add_atom_watch(modem, OFONO_ATOM_TYPE_VOICECALL,
316 voicecall_watch, modem, NULL);
319 static void call_modemwatch(struct ofono_modem *modem, void *user)
321 modem_watch(modem, TRUE, user);
324 static int hfp_ag_init(void)
326 DBusConnection *conn = ofono_dbus_get_connection();
328 if (DBUS_TYPE_UNIX_FD < 0)
331 /* Registers External Profile handler */
332 if (!g_dbus_register_interface(conn, HFP_AG_EXT_PROFILE_PATH,
333 BLUEZ_PROFILE_INTERFACE,
334 profile_methods, NULL,
336 ofono_error("Register Profile interface failed: %s",
337 HFP_AG_EXT_PROFILE_PATH);
341 sim_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
343 modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
344 __ofono_modem_foreach(call_modemwatch, NULL);
346 connection_hash = g_hash_table_new_full(g_str_hash, g_str_equal,
347 g_free, connection_destroy);
352 static void hfp_ag_exit(void)
354 DBusConnection *conn = ofono_dbus_get_connection();
356 __ofono_modemwatch_remove(modemwatch_id);
357 g_dbus_unregister_interface(conn, HFP_AG_EXT_PROFILE_PATH,
358 BLUEZ_PROFILE_INTERFACE);
360 g_hash_table_destroy(connection_hash);
363 g_hash_table_foreach_remove(sim_hash, sim_watch_remove, NULL);
364 g_hash_table_destroy(sim_hash);
367 OFONO_PLUGIN_DEFINE(hfp_ag_bluez5, "Hands-Free Audio Gateway Profile Plugins",
368 VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
369 hfp_ag_init, hfp_ag_exit)