diff options
Diffstat (limited to 'serial/manager.c')
-rw-r--r-- | serial/manager.c | 142 |
1 files changed, 135 insertions, 7 deletions
diff --git a/serial/manager.c b/serial/manager.c index 36711c1a..6e46bc51 100644 --- a/serial/manager.c +++ b/serial/manager.c @@ -48,14 +48,34 @@ #define PATH_LENGTH 32 #define BASE_UUID "00000000-0000-1000-8000-00805F9B34FB" +struct pending_connection { + DBusConnection *conn; + DBusMessage *msg; + bdaddr_t src; /* Source address */ + char *addr; /* Destination address */ + char *adapter_path; /* Adapter D-Bus path */ +}; + static DBusConnection *connection = NULL; -static DBusHandlerResult err_invalid_args(DBusConnection *conn, +static void pending_connection_free(struct pending_connection *pc) +{ + if (pc->conn) + dbus_connection_unref(pc->conn); + if (pc->msg) + dbus_message_unref(pc->msg); + if (pc->addr) + g_free(pc->addr); + if (pc->adapter_path) + g_free(pc->adapter_path); +} + +static DBusHandlerResult err_connection_failed(DBusConnection *conn, DBusMessage *msg, const char *str) { return send_message_and_unref(conn, dbus_message_new_error(msg, - SERIAL_ERROR_INTERFACE ".InvalidArguments", str)); + SERIAL_ERROR_INTERFACE".ConnectionAttemptFailed", str)); } static DBusHandlerResult err_failed(DBusConnection *conn, @@ -66,11 +86,100 @@ static DBusHandlerResult err_failed(DBusConnection *conn, SERIAL_ERROR_INTERFACE ".Failed", str)); } +static DBusHandlerResult err_invalid_args(DBusConnection *conn, + DBusMessage *msg, const char *str) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + SERIAL_ERROR_INTERFACE ".InvalidArguments", str)); +} + +static DBusHandlerResult err_not_supported(DBusConnection *conn, + DBusMessage *msg) +{ + return send_message_and_unref(conn, + dbus_message_new_error(msg, + SERIAL_ERROR_INTERFACE ".NotSupported", + "The service is not supported by the remote device")); +} + +static void handles_reply(DBusPendingCall *call, void *data) +{ + struct pending_connection *pc = data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + uint32_t *phandle; + int len; + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + if (dbus_error_has_name(&derr, + "org.bluez.Error.ConnectionAttemptFailed")) + err_connection_failed(pc->conn, pc->msg, derr.message); + else + err_not_supported(pc->conn, pc->msg); + + error("GetRemoteServiceHandles: %s(%s)", + derr.name, derr.message); + goto fail; + } + + if (!dbus_message_get_args(reply, &derr, + DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &phandle, + &len, DBUS_TYPE_INVALID)) { + err_not_supported(pc->conn, pc->msg); + error("%s: %s", derr.name, derr.message); + goto fail; + } + + if (len == 0) { + err_not_supported(pc->conn, pc->msg); + goto fail; + } + + /* FIXME: Get the record */ + dbus_message_unref(reply); + return; +fail: + dbus_message_unref(reply); + dbus_error_free(&derr); + pending_connection_free(pc); +} + +static int get_handles(struct pending_connection *pc, const char *uuid, + DBusPendingCallNotifyFunction cb) +{ + DBusMessage *msg; + DBusPendingCall *pending; + + msg = dbus_message_new_method_call("org.bluez", pc->adapter_path, + "org.bluez.Adapter", "GetRemoteServiceHandles"); + if (!msg) + return -1; + + dbus_message_append_args(msg, + DBUS_TYPE_STRING, &pc->addr, + DBUS_TYPE_STRING, &uuid, + DBUS_TYPE_INVALID); + + if (dbus_connection_send_with_reply(pc->conn, msg, &pending, -1) == FALSE) { + error("Can't send D-Bus message."); + return -1; + } + + dbus_pending_call_set_notify(pending, cb, pc, NULL); + dbus_message_unref(msg); + dbus_pending_call_unref(pending); + + return 0; +} + static DBusHandlerResult connect_service(DBusConnection *conn, DBusMessage *msg, void *data) { DBusError derr; bdaddr_t src; + struct pending_connection *pc; const char *addr, *pattern; char *endptr; long val; @@ -92,6 +201,14 @@ static DBusHandlerResult connect_service(DBusConnection *conn, if ((dev_id < 0) || (hci_devba(dev_id, &src) < 0)) return err_failed(conn, msg, "Adapter not available"); + pc = g_new0(struct pending_connection, 1); + pc->conn = dbus_connection_ref(conn); + pc->msg = dbus_message_ref(msg); + bacpy(&pc->src, &src); + pc->addr = g_strdup(addr); + pc->adapter_path = g_malloc0(16); + snprintf(pc->adapter_path, 16, "/org/bluez/hci%d", dev_id); + /* UUID 128*/ if (strlen(pattern) == 36) { char tmp[37]; @@ -102,10 +219,15 @@ static DBusHandlerResult connect_service(DBusConnection *conn, tmp[6] = '0'; tmp[7] = '0'; - if (strcasecmp(BASE_UUID, tmp) != 0) + if (strcasecmp(BASE_UUID, tmp) != 0) { + pending_connection_free(pc); return err_invalid_args(conn, msg, "invalid UUID"); + } - /* FIXME: Retrieve the handle/record */ + if (get_handles(pc, pattern, handles_reply) < 0) { + pending_connection_free(pc); + return err_not_supported(conn, msg); + } return DBUS_HANDLER_RESULT_HANDLED; } @@ -113,22 +235,28 @@ static DBusHandlerResult connect_service(DBusConnection *conn, errno = 0; val = strtol(pattern, &endptr, 0); if ((errno == ERANGE && (val == LONG_MAX || val == LONG_MIN)) || - (errno != 0 && val == 0) || (pattern == endptr)) + (errno != 0 && val == 0) || (pattern == endptr)) { + pending_connection_free(pc); return err_invalid_args(conn, msg, "Invalid pattern"); + } /* Record handle: starts at 0x10000 */ if (strncasecmp("0x", pattern, 2) == 0) { - if (val < 0x10000) + if (val < 0x10000) { + pending_connection_free(pc); return err_invalid_args(conn, msg, "invalid record handle"); + } /* FIXME: retrieve the record */ return DBUS_HANDLER_RESULT_HANDLED; } /* RFCOMM Channel range: 1 - 30 */ - if (val < 1 || val > 30) + if (val < 1 || val > 30) { + pending_connection_free(pc); return err_invalid_args(conn, msg, "invalid RFCOMM channel"); + } /* FIXME: Connect */ info("Connecting to channel: %d", val); |