From 1d349ded8c38831b36600f53a8dd9b781d9e13eb Mon Sep 17 00:00:00 2001 From: Gustavo F. Padovan Date: Fri, 8 Jan 2010 17:25:43 -0200 Subject: [PATCH] Add HFP support in oFono It uses BlueZ through to get HFP working following the org.bluez.HandsfreeGateway and org.bluez.HandsfreeAgent from the BlueZ D-Bus API. You need the HFP patch into BlueZ too. Many thanks to Zhenhua Zhang for its prototype on this code. --- drivers/hfpmodem/hfpmodem.h | 2 + gatchat/gattty.c | 23 ++++ gatchat/gattty.h | 1 + include/modem.h | 2 + plugins/hfp.c | 284 +++++++++++++++++++++++++++++++++++++++++-- plugins/modemconf.c | 3 +- src/modem.c | 15 +++ 7 files changed, 318 insertions(+), 12 deletions(-) diff --git a/drivers/hfpmodem/hfpmodem.h b/drivers/hfpmodem/hfpmodem.h index 509846b..5fee68f 100644 --- a/drivers/hfpmodem/hfpmodem.h +++ b/drivers/hfpmodem/hfpmodem.h @@ -63,11 +63,13 @@ enum hfp_indicator { struct hfp_data { GAtChat *chat; + char *handsfree_path; unsigned int ag_features; unsigned int ag_mpty_features; unsigned int hf_features; unsigned char cind_pos[HFP_INDICATOR_LAST]; unsigned int cind_val[HFP_INDICATOR_LAST]; + unsigned int at_timeout; }; extern void hfp_netreg_init(); diff --git a/gatchat/gattty.c b/gatchat/gattty.c index 02ca389..ae12662 100644 --- a/gatchat/gattty.c +++ b/gatchat/gattty.c @@ -260,3 +260,26 @@ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options) return channel; } + +GIOChannel *g_at_get_channel_from_fd(int fd) +{ + GIOChannel *channel; + struct termios ti; + + /* Switch TTY to raw mode */ + memset(&ti, 0, sizeof(ti)); + cfmakeraw(&ti); + tcflush(fd, TCIOFLUSH); + tcsetattr(fd, TCSANOW, &ti); + + channel = g_io_channel_unix_new(fd); + + if (channel == NULL) { + close(fd); + return NULL; + } + + g_io_channel_set_close_on_unref(channel, TRUE); + + return channel; +} diff --git a/gatchat/gattty.h b/gatchat/gattty.h index dc3fe16..730f1dc 100644 --- a/gatchat/gattty.h +++ b/gatchat/gattty.h @@ -43,6 +43,7 @@ extern "C" { */ GIOChannel *g_at_tty_open(const char *tty, GHashTable *options); +GIOChannel *g_at_get_channel_from_fd(int fd); #ifdef __cplusplus } #endif diff --git a/include/modem.h b/include/modem.h index b2aa607..bf28515 100644 --- a/include/modem.h +++ b/include/modem.h @@ -47,6 +47,8 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type); int ofono_modem_register(struct ofono_modem *modem); void ofono_modem_remove(struct ofono_modem *modem); +struct ofono_modem *ofono_modem_get_by_path(const char *path); + void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered); ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem); diff --git a/plugins/hfp.c b/plugins/hfp.c index 3bbd922..1e4eeb3 100644 --- a/plugins/hfp.c +++ b/plugins/hfp.c @@ -30,6 +30,8 @@ #include #include #include +#include +#include #define OFONO_API_SUBJECT_TO_CHANGE #include @@ -52,11 +54,24 @@ #include +#include + +#define BLUEZ_SERVICE "org.bluez" +#define BLUEZ_PATH "/" +#define BLUEZ_MANAGER_INTERFACE "org.bluez.Manager" +#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter" +#define BLUEZ_DEVICE_INTERFACE "org.bluez.Device" +#define BLUEZ_GATEWAY_INTERFACE "org.bluez.HandsfreeGateway" + +#define HFP_AGENT_INTERFACE "org.bluez.HandsfreeAgent" + static const char *brsf_prefix[] = { "+BRSF:", NULL }; static const char *cind_prefix[] = { "+CIND:", NULL }; static const char *cmer_prefix[] = { "+CMER:", NULL }; static const char *chld_prefix[] = { "+CHLD:", NULL }; +static DBusConnection *connection; + static int hfp_disable(struct ofono_modem *modem); static void hfp_debug(const char *str, void *user_data) @@ -135,6 +150,60 @@ static void cmer_cb(gboolean ok, GAtResult *result, gpointer user_data) sevice_level_conn_established(modem); } +static int send_method_call(const char *dest, const char *path, + const char *interface, const char *method, + DBusPendingCallNotifyFunction cb, + void *user_data, int type, ...) +{ + DBusMessage *msg; + DBusPendingCall *call; + va_list args; + + msg = dbus_message_new_method_call(dest, path, interface, method); + if (!msg) { + ofono_error("Unable to allocate new D-Bus %s message", method); + return -ENOMEM; + } + + va_start(args, type); + + if (!dbus_message_append_args_valist(msg, type, args)) { + dbus_message_unref(msg); + va_end(args); + return -EIO; + } + + va_end(args); + + if (!cb) { + g_dbus_send_message(connection, msg); + return 0; + } + + if (!dbus_connection_send_with_reply(connection, msg, &call, -1)) { + ofono_error("Sending %s failed", method); + dbus_message_unref(msg); + return -EIO; + } + + dbus_pending_call_set_notify(call, cb, user_data, NULL); + dbus_pending_call_unref(call); + dbus_message_unref(msg); + + return 0; +} + +static gboolean hfp_enable_timeout(gpointer user) +{ + struct ofono_modem *modem = user; + + if (ofono_modem_get_powered(modem)) + return FALSE; + + hfp_disable(modem); + return FALSE; +} + static void cind_status_cb(gboolean ok, GAtResult *result, gpointer user_data) { @@ -260,8 +329,7 @@ error: } /* either oFono or Phone could request SLC connection */ -static int service_level_connection(struct ofono_modem *modem, - const char *tty) +static int service_level_connection(struct ofono_modem *modem, int fd) { struct hfp_data *data = ofono_modem_get_data(modem); GIOChannel *io; @@ -269,7 +337,7 @@ static int service_level_connection(struct ofono_modem *modem, GAtChat *chat; char buf[64]; - io = g_at_tty_open(tty, NULL); + io = g_at_get_channel_from_fd(fd); if (!io) { ofono_error("Service level connection failed: %s (%d)", strerror(errno), errno); @@ -296,9 +364,158 @@ static int service_level_connection(struct ofono_modem *modem, return -EINPROGRESS; } +static DBusMessage *hfp_agent_new_connection(DBusConnection *conn, DBusMessage *msg, void *data) +{ + int fd; + const char *path; + struct ofono_modem *modem = NULL; + + path = dbus_message_get_path(msg); + modem = ofono_modem_get_by_path(path); + + if (!modem) + return NULL; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UNIX_FD, &fd, + DBUS_TYPE_INVALID)) + return __ofono_error_invalid_args(msg); + + service_level_connection(modem, fd); + + return NULL; +} + +static DBusMessage *hfp_agent_release(DBusConnection *conn, DBusMessage *msg, void *data) +{ + return NULL; +} + +static GDBusMethodTable agent_methods[] = { + { "NewConnection", "h", "", hfp_agent_new_connection, + G_DBUS_METHOD_FLAG_ASYNC }, + { "Release", "", "", hfp_agent_release }, + {NULL, NULL, NULL, NULL} +}; + +static void find_device_cb(DBusPendingCall *call, gpointer user_data) +{ + DBusError err; + DBusMessage *reply; + struct ofono_modem *modem = user_data; + struct hfp_data *data = ofono_modem_get_data(modem); + const char *device, *obj_path; + int ret; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&err); + + if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, + &device, DBUS_TYPE_INVALID) == FALSE) { + if (dbus_error_is_set(&err) == TRUE) { + ofono_error("%s", err.message); + dbus_error_free(&err); + } + goto done; + } + + ofono_debug("Using device %s", device); + data->handsfree_path = g_strdup(device); + + obj_path = ofono_modem_get_path(modem); + + ret = send_method_call(BLUEZ_SERVICE, device, + BLUEZ_GATEWAY_INTERFACE, "RegisterAgent", + NULL, NULL, DBUS_TYPE_OBJECT_PATH, + &obj_path, DBUS_TYPE_INVALID); + + if (ret < 0) + ofono_error("agent_register failed(%d)", ret); + +done: + dbus_message_unref(reply); +} + +static void get_adapter_cb(DBusPendingCall *call, gpointer user_data) +{ + DBusError err; + DBusMessage *reply; + struct ofono_modem *modem = user_data; + const char *adapter = NULL; + const char *address; + int ret; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&err); + if (dbus_message_get_args(reply, &err, + DBUS_TYPE_OBJECT_PATH, + &adapter, DBUS_TYPE_INVALID) == FALSE) { + if (adapter == NULL) { + ofono_error("bluetooth adapter is not enabled"); + dbus_error_free(&err); + goto done; + } + + if (dbus_error_is_set(&err) == TRUE) { + ofono_error("%s %s", adapter, err.message); + dbus_error_free(&err); + } + goto done; + } + + address = ofono_modem_get_string(modem, "Address"); + + DBG("address %s", address); + + if (address == NULL) + return; + + ret = send_method_call(BLUEZ_SERVICE, adapter, + BLUEZ_ADAPTER_INTERFACE, "FindDevice", + find_device_cb, modem, + DBUS_TYPE_STRING, &address, + DBUS_TYPE_INVALID); + + if (ret < 0) + ofono_error("find_device failed(%d)", ret); + +done: + dbus_message_unref(reply); + +} + +static int hfp_register_ofono_handsfree(struct ofono_modem *modem) +{ + ofono_debug("Register oFono Agent to bluetooth daemon"); + + return send_method_call(BLUEZ_SERVICE, BLUEZ_PATH, + BLUEZ_MANAGER_INTERFACE, "DefaultAdapter", + get_adapter_cb, modem, + DBUS_TYPE_INVALID); +} + +static int hfp_unregister_ofono_handsfree(struct ofono_modem *modem) +{ + const char *obj_path = ofono_modem_get_path(modem); + struct hfp_data *data = ofono_modem_get_data(modem); + + ofono_debug("Unregister oFono Agent to bluetooth daemon"); + + if (!data->handsfree_path) + return -EINVAL; + + return send_method_call(BLUEZ_SERVICE, data->handsfree_path, + BLUEZ_GATEWAY_INTERFACE, "UnregisterAgent", + NULL, NULL, DBUS_TYPE_OBJECT_PATH, + &obj_path, DBUS_TYPE_INVALID); +} + static int hfp_probe(struct ofono_modem *modem) { struct hfp_data *data; + const char *obj_path; + DBusConnection *conn = ofono_dbus_get_connection(); data = g_try_new0(struct hfp_data, 1); if (!data) @@ -312,39 +529,81 @@ static int hfp_probe(struct ofono_modem *modem) ofono_modem_set_data(modem, data); + connection = dbus_connection_ref(conn); + + obj_path = ofono_modem_get_path(modem); + g_dbus_register_interface(conn, obj_path, HFP_AGENT_INTERFACE, + agent_methods, NULL, NULL, data, NULL); + + if (hfp_register_ofono_handsfree(modem) != 0) + return -EINVAL; + return 0; } static void hfp_remove(struct ofono_modem *modem) { - gpointer data = ofono_modem_get_data(modem); + struct hfp_data *data = ofono_modem_get_data(modem); + + hfp_unregister_ofono_handsfree(modem); + + if (data->handsfree_path) + g_free(data->handsfree_path); if (data) g_free(data); + dbus_connection_unref(connection); + ofono_modem_set_data(modem, NULL); } +static int hfp_connect_ofono_handsfree(struct ofono_modem *modem) +{ + struct hfp_data *data = ofono_modem_get_data(modem); + + ofono_debug("Connect to bluetooth daemon"); + + if (!data->handsfree_path || !connection) + return -EINVAL; + + return send_method_call(BLUEZ_SERVICE, data->handsfree_path, + BLUEZ_GATEWAY_INTERFACE, "Connect", + NULL, NULL, DBUS_TYPE_INVALID); +} + /* power up hardware */ static int hfp_enable(struct ofono_modem *modem) { - const char *tty; - int ret; + struct hfp_data *data = ofono_modem_get_data(modem); DBG("%p", modem); - tty = ofono_modem_get_string(modem, "Device"); - if (tty == NULL) + data->at_timeout = + g_timeout_add_seconds(10, hfp_enable_timeout, modem); + + if (hfp_connect_ofono_handsfree(modem) < 0) return -EINVAL; - ret = service_level_connection(modem, tty); + return -EINPROGRESS; +} + +static int hfp_disconnect_ofono_handsfree(struct ofono_modem *modem) +{ + struct hfp_data *data = ofono_modem_get_data(modem); - return ret; + if (!data->handsfree_path || !connection) + return -EINVAL; + + return send_method_call(BLUEZ_SERVICE, data->handsfree_path, + BLUEZ_GATEWAY_INTERFACE, "Disconnect", + NULL, NULL, DBUS_TYPE_INVALID); } static int hfp_disable(struct ofono_modem *modem) { struct hfp_data *data = ofono_modem_get_data(modem); + GSource *source; DBG("%p", modem); @@ -359,6 +618,11 @@ static int hfp_disable(struct ofono_modem *modem) ofono_modem_set_powered(modem, FALSE); + source = g_main_context_find_source_by_id(NULL, data->at_timeout); + if (source) + g_source_destroy(source); + + hfp_disconnect_ofono_handsfree(modem); return 0; } diff --git a/plugins/modemconf.c b/plugins/modemconf.c index c8c261f..b4c1600 100644 --- a/plugins/modemconf.c +++ b/plugins/modemconf.c @@ -124,12 +124,11 @@ static struct ofono_modem *create_modem(GKeyFile *keyfile, const char *group) modem = ofono_modem_create(group, driver); - if (!g_strcmp0(driver, "phonesim")) + if (!g_strcmp0(driver, "phonesim") || !g_strcmp0(driver, "hfp")) set_address(modem, keyfile, group); if (!g_strcmp0(driver, "atgen") || !g_strcmp0(driver, "g1") || !g_strcmp0(driver, "calypso") || - !g_strcmp0(driver, "hfp") || !g_strcmp0(driver, "palmpre")) set_device(modem, keyfile, group); diff --git a/src/modem.c b/src/modem.c index bbc9905..9fab722 100644 --- a/src/modem.c +++ b/src/modem.c @@ -1074,6 +1074,21 @@ struct ofono_modem *ofono_modem_create(const char *name, const char *type) return modem; } +struct ofono_modem *ofono_modem_get_by_path(const char *path) +{ + GSList *l; + struct ofono_modem *modem; + + for (l = g_modem_list; l; l = l->next) { + modem = l->data; + + if (!g_strcmp0(modem->path, path)) + return modem; + } + + return NULL; +} + static void emit_modems() { DBusConnection *conn = ofono_dbus_get_connection(); -- 1.6.4.4